“Bad weather” pretty much summarizes the contest for me. Both in the ionosphere and lower. Poor propagation overall and thunder storms circling my QTH. The poor propagation also drove people to other bands (except those of us who had decided to do single band). Whenever the band opened slightly to some direction, for sure there was a thunder storm in that direction and terrible QRN. My ears are still ringing from those lightning crashes – volume had to be turned up and APF on in order to copy stations. I do not want to wear headphones for a while… One could argue that it must be the lightning that fires up the ionosphere 🙂
Finally, late on Sunday afternoon the band opened to EU but then the QRN prevented me from working most of the stations. Nothing more frustrating than hearing a bunch of people calling but I can’t copy them. Then there were some minor episodes like losing the power for a bit during the contest, AC leaking condensed water into my ham shack and me being attacked by a fly swarm while trying to work. The latter was very annoying and distracting! They must have been attracted to my “contest aroma” (sweating with the AC off and no shower). And when the contest was almost over (1.5 hour left), I hear a loud rumble through my headphones and realized that a thunder storm had just developed almost exactly above my QTH. Turned off everything and pulled cables off. I kept waiting for it to go away but no, it decided not to go anywhere (game over). The timing of WPX is really bad for us due to the storm season but what can we do?
But I still managed to get 1252 QSOs and 706 prefixes. However, I am pretty sure that there are tons of copy errors because of the QRN and poor band condx (very weak signals) but that’s life…
Author Archives: eloranta
Multi program action example
Here is an example where in one QSO (SP5EXA) RX A decoded the first message and RX B the following message. So, the QSO would not have completed (or, well, could have after retries) without the two RXs.

Here RX A was connected to inverted L and RX B to BOG-NE. The ! sign indicates that the decode was received only with that RX.
Factors affecting how BOG antennas work
My half empirical points for successful BOG antenna deployment:
- Poor ground conductivity is good for BOGs
- Do not rise the antenna wire too high from the ground (< 2″). This includes grass and other vegitation on the ground. Rising it too high can decrease the directivity.
- If you install radials, make sure that they are not too long. Three 40′ radials per end point seems to be good for 60/80/160.
- Length: 150′ – 300′ has been often used. I tried 150′, 200′ and 300′. 300′ worked best in my case. It has good pattern on 160 m, 80 m, and 60 m. I have not tried it on 40 m since I have 3 element beam there.
There are many stories online where some people say that BOGs work great and others that have absolutely no success with these antennas. It took me a while to test various setups but now I finally have a working system. It easily beats my inverted L in receiving on 160/80/60 especially when lightning is present somewhere. But I tend to use both in receiving (see other posts on “multi”).
RX antenna switch installed
I installed remote RX antenna switch (RAS-4) to select one of my beverage antennas (NW/SE/NE/SW beverages). It is driven by Hamation (former Array Solutions) Bandmaster V with homebrew software.

WSJT-X time adjustment tool for Linux
Wrote time adjustment tool for WSJT-X on Linux (gtk+). It can change system time and interact with the NTP (or equivalent) services to turn on/off automatic clock updating:

I tested it on Arch Linux but it should work on other distros as well. One annoying pending issue is still that when turning NTP back on, it prompts for root password. The code can be found from github.com:
https://github.com/jmeloranta/timeadj
Compile with: make
And install with: make install
By default it installs under /usr/local directory. Needs gtk+ graphics library.
WSJT-X RX aggregator (AKA “multi”)
My Elecraft K4D has two receivers that can be used for diversity receive. In order to improve my FT8 RX capabilities on 160 m and 80 m bands, I wanted to fully utilize my antennas (inverted L and Beverage on ground receiving antennas) in kind of “FT8 diversity” set up. Note that these two antennas have different polarizations as well as differences in their radiation patterns. Since there is always some small audio phase difference between the two RXs, it is not a good idea to try to add them together to form a single audio stream and then decode the resultant signal with WSJT-X. Instead, I decided to feed audio from the two RXs into two separate decoders. But, of course, WSJT-X does not currently support this kind of setup. This is kind of “diversity” receive within the limitation of the 15 second RX period of FT8. The quotes emphasize that this is not the same diversity receive that one can do on CW/SSB but corresponds to two separate simultaneous decodes using the two RXs.
In the first iteration I ran two separate WSJT-X instances – one for each RX. While it did work, it was a rather clumsy approach. One of the WSJT-X instances was the master (responsible for running QSO) and the second one was just receiving. If only the second RX received a decode but the first one did not then one would have to manually account for this during QSO. Very easy to mess things up! By the way, this is a great way to compare antennas (for FT8 mode). However, the situation with true diversity receive on CW/SSB may be different with those antennas. In other words, the mode must always be considered when comparing antennas.
So, I decided to write a “middle man” program that sits between WSJT-X GUI and the decoder (program called JT9). This still requires running two WSJT-X (2nd with the -r option) instances so that they both feed their audio data to the corresponding shared memory blocks. The second WSJT-X (“slave”) just hangs around and does nothing. The first WSJT-X (“master”) instance gets all the decodes from the middle man. Normally JT9 picks up the data from these blocks, performs decode, and reports back to WSJT-X. In order to have everything passed to the same WSJT-X, the middle man program must get audio data from both WSJT-X instances, run JT9 on them, combine the decodes and report back to the master WSJT-X. WSJT-X eliminates multiple decodes that it receives, so the middle man should eliminate duplicates that come from both RXs in such way that the weakest SNR decode is discarded. Another decode that must be eliminated is my own transmission which appears on the audio feed of the second RX. To indicate where the decode came from, labels ‘a’, ‘b’, or ‘=’ are added to the decoded lines (replaces the ~ character in the original format output). Label ‘a’ means that the decode from the first RX was used (as it had the best SNR), ‘b’ it came from the second RX and the equal sign indicates that both were equally good. I may add the dB difference in the output at some point. But that information is already available by runinng two separate WSJT-X instances.
The code I came up with is on github: https://github.com/jmeloranta/multi . Since the JT9 executable path is hardcoded into WJST-X GUI, the installation is a bit clumsy, see install-multi script.
Be careful not to overwrite the original jt9 program (perhaps make a backup copy). Then start two WSJT-X processes (the default settings assume -r 2; determines the shared memory block name):
$ (wsjtx &); (wsjtx -r 2 &)
This is done in script called start-multi. The first one should have audio input set to left stereo channel and the second right stereo channel. The radio needs to be in the diversity receive mode so that both RXs are active and have the same behavior. The slave WSJT-X can be minimized (but do not quit it). QSOs can be run normally using the master WSJT-X. Adjust the audio levels in both WSJT-X’s so that they are about the same. Also, need to turn off rig control on the slave WSJT-X – otherwise it will clash with the master.
This is what it looks like:

The above test (sorry about the fuzzy image) was run on 10m with the two RXs having different audio levels in order to have some difference between them. I will find out tonight how this will work on 160 m and 80 m. Update: Since it is summer time, there is lightning all over the place. At this time of the year my BOG antenna beats the inverted L in receiving pretty much all the time. During winter time the situation will be different though.
The current signal indicator limits are:
= means both RXs are within 2 dB of each other
a means that RX a is more than 2 dB better than b
b means that RX b is more than 2 dB better than a
A means that RX a is more than 8 dB better than b
B means that RX b is more than 8 dB better than a
Additionally, if either RX misses the decode completely, this is indicated by adding ‘!’ character after the above indicator.
The actual S/N numbers can be recorded by specifying COLLECT_STATS environment variable in start-multi script.

The top waterfall corresponds to RX a and the bottom to RX b.
Also, there are now two helper scripts to set up a separate WSJT-X installation under /usr/local that won’t interfere with the main installation (see the github repo). Read those scripts before running them! But the basic installation goes (installer works only on Arch Linux):
$ make
$ sudo ./install-multi
Before using start-multi script, change your call sign in it & see if you want to use some of the options there. To start the program use (remember to configure both WSJT-X instances; see above):
$ start-multi
NOTE: The filtering capability of WSJT-X improved does not work well with the “middle man” (multi). I don’t know why but you need to disable the filters. There is a built-in filter capability in multi (see FILTER and CONTINENT environment variables in start-multi script). Since two decoders are run simultaneously, it may be a good idea to only use 2-stage decoding as 3-stage can be too slow (and lead to missed decodes).
BOG NEC
Although NEC is not a terribly reliable model for beverage on ground, here are couple of screen shots from OH6CK (eznec) for “entertainment”:


And, yes, radikaalit should be radiaalit. The spell checker strikes again.
NW / SE BOG area cleared
Cleared up & installed another 300′ reversible BOG along NW / SE direction (black/red antenna wire on the ground):

Still need to apply some round up to keep the greenies at bay. At this point, I lead my dog in the number of ticks 5 to 4 ! Won’t be going back there before I get proper tick spray… Except I could not stay away. Installed the ground rod and radials. Next remote RX antenna switch.
Bandmaster V hacking
I improved the antenna status widget to also change antennas for bands. The protocol for this device has not been published, so I had to snoop on the RS-232 traffic to find out how to talk to it. Here is the new code:
#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 "BOG NE"
#define ANTENNA7 "BOG SW"
#define ANTENNA8 "BOG NW"
#define ANTENNA9 "BOG SE"
#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"};
/*
* RX antenna control:
*
* BOG NE = antenna 6 (no antenna; no relays selected)
* BOG SW = antenna 7 (select relay 9)
* BOG NW = antenna 8 (select relay 10)
* BOG SE = antenna 9 (select relay 11)
*
* BOG selected for can be selected for 160, 80, 75, and 60. TX antenna is unaffected (radio -> acom -> inv L)
*
* Numbers below (6, 7, 8, 9) refer to antenna numbers defined in bandmaster V. Bands 40 - 6m are always
* unchanged. Remember bandmaster uses hexadecimal everywhere.
*
*/
char *bogs[] = {"[R29T09H060606060402030203020301]\r", "[R29T09H070707070402030203020301]\r",
"[R29T09H080808080402030203020301]\r", "[R29T09H090909090402030203020301]\r"};
int fd, tx_on = 0;
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, ¶m) < 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(¶m, B57600);
cfsetospeed(¶m, B57600);
if(tcsetattr(fd, TCSANOW, ¶m) < 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];
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, "%2x01EEEEEEEEEEEE%4x", &antenna, &relay) != 2) {
printf("Error extracting antenna & relay information.");
antenna = 99;
}
if(antenna > 0 && antenna < MAX_ANTENNAS)
sprintf(buf, "<span color='blue'>%s (relays = %x)</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
tx_on = 1;
} else tx_on = 0;
if(antenna >= 0 && antenna < MAX_SM)
sprintf(buf, "<span color='blue'>%s (%s)</span>", stack_match[antenna], tx_on?"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();
}
void bog_ne(GtkWidget *win, gpointer data) {
if(!tx_on) {
printf("BOG NE selected\n");
write(fd, bogs[0], strlen(bogs[0]));
}
}
void bog_sw(GtkWidget *win, gpointer data) {
if(!tx_on) {
printf("BOG SW selected\n");
write(fd, bogs[1], strlen(bogs[1]));
}
}
void bog_nw(GtkWidget *win, gpointer data) {
if(!tx_on) {
printf("BOG NW selected\n");
write(fd, bogs[2], strlen(bogs[2]));
}
}
void bog_se(GtkWidget *win, gpointer data) {
if(!tx_on) {
printf("BOG SE selected\n");
write(fd, bogs[3], strlen(bogs[3]));
}
}
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *ant_label, *sm_label, *hbox1, *hbox2, *hbox3, *vbox, *button1, *button2, *button3, *button4;
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, 80);
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);
hbox3 = 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);
button1 = gtk_button_new_with_label("BOG NE");
button2 = gtk_button_new_with_label("BOG SW");
button3 = gtk_button_new_with_label("BOG NW");
button4 = gtk_button_new_with_label("BOG SE");
gtk_box_pack_start(GTK_BOX(hbox3), button1, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(hbox3), button2, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(hbox3), button3, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(hbox3), button4, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(vbox), hbox3, TRUE, TRUE, 5);
gtk_container_add(GTK_CONTAINER(window), vbox);
g_signal_connect(button1, "clicked", G_CALLBACK(bog_ne), NULL);
g_signal_connect(button2, "clicked", G_CALLBACK(bog_sw), NULL);
g_signal_connect(button3, "clicked", G_CALLBACK(bog_nw), NULL);
g_signal_connect(button4, "clicked", G_CALLBACK(bog_se), NULL);
if((fd = open(DEVICE, O_RDWR)) < 0) {
fprintf(stderr, "Can't open device.\n");
exit(1);
}
set_tty();
bog_ne(NULL, NULL); // BOG NE is the default on start up
g_timeout_add(UPDATE, update_band_info, NULL);
gtk_widget_show_all(window);
gtk_main();
close(fd);
return 0;
}
In addition to performing automatic antenna switching for my tower (40 m – 6 m), it now can switch between four receive antennas for 160 m – 60m. You probably guessed that these are four BOGs oriented NE, SW, NW, SE.
The previously described ft8cmp code and the above code are now both on github.com:
https://github.com/jmeloranta/ft8cmp.git
https://github.com/jmeloranta/antenna-widget.git
My BOG antenna finally works!
The KD9SV reversible beverage on ground (RBOG) now finally works! Tested on 160 m, 80 m, and 60 m towards EU. It beats my inverted L on both 160 and 80 m bands. The antenna also now shows proper F/B (switching between forward and reverse; 10-12 dB).
The antenna is 300′ long made out of non-twisted pair cable (18 AWG) laid along the ground. Both ends have 4′ ground rod + three 40′ radials. The feed points have mix 31 ferrite rings with 9 rounds of the RG6 coax wrapped around them (choke).
Here is a quick test on 80 m FT8 (evening; band open to EU):

Some stations that RBOG did not hear (pointed towards EU) were located in south America. The current RBOG is oriented NE/SW and I need to add another one for NW/SE. For explanation of the graph, see the earlier blog post.
