Antenna rotator control program for Raspberry Pi and WSJT-X


For the digital modes traffic, I run WSJT-X on a Raspberry Pi (4B). So, no need to devote a “big” computer for that purpose, the Raspberry does the job perfectly well, and for less power consumption and noise. For my 144 MHz moonbounce operations, I wanted a simple system to drive my existing ERC-3D rotator controller (by DF9GR). That model is now obsolete but works flawlessly and I wanted to keep it as-is, without changing anything. The ERC-3D is linked to azimuth (Yaesu G650) and elevation (Kenpro KR500) antenna rotators.

Rotator Controller

The search for a ready made program I did on the Internet was not successful, since I wanted to keep as-is my exisiting system. Having in mind that WSJT-X provides an azel.dat file including the coordinates of the moon, I decided to write a program to extract these data and to use them to command my ERC-3D, in first place to track the moon.

To be able to do it, a prerequisite is to have Hamlib installed on the Raspberry. What is hamlib : “the Ham Radio Control Library –Hamlib, for short– is a project to provide programs with a consistent Application Programming Interface (API) for controlling the myriad of radios and rotators available to amateur radio and communications users”. To date the current hamlib version is 4.2. and can be downloaded here. How to install it on the Raspberry is out of the present scope, there a several tutorials on the Internet that clearly explain it (it is very easy).

Once Hamlib was installed, the program had to be written (it is available at the end of this post). It has been written in Python 3 and, since I’m not at all a programmer, it is my friend Didier (ON4KDV), who wrote the backbone of the program (many thanks to him !). I then customized it and added some features, a.o. the possibility to command the rotator controller not only to automatically track the moon but also to rotate/elevate the antennas wherever needed.

The program is named “Rotator Controller” and an icon placed on the desktop of the Raspberry allows to launch it easily. It opens in the command-line terminal.

Here is below how it works.

First you select the USB port to be used and then you have the choice :

  • Manual rotation/elevation
  • Automatic tracking of the moon, as from the azel.dat file out of WSJT-X
  • Exit

We start first with the manual rotation/elevation. The possible options are self-explanatory. See also the code at the end, it provides many explanations (comments) too.

Rotator Controller 1

When the option “r” is selected, if it was elevated before, the antenna will come back to the horizon (0° elevation) and towards the wished azimuth. “s” will stop immediately any rotation/elevation.

On the example here, we set the azimuth to 113°. Then we ask for the position (“p”) and we see that the antenna is effectively well on Az. 113°.

Rotator Controller 2

Then we select “l” and we set Az. to 113° and El. to 10°. Requesting the position by typing “p”, we get well 113° Az. and 10° El.

Rotator Controller 3

To come back to the main menu, you have to exit (“e”) and restart the program. Before restarting the program, we must start first WSJT-X, so that the azel.dat file is available. We then restart the program and we select a USB port not already in use by WSJT-X (for the CAT for example).

We select “a” for the automatic moon tracking. We are prompted to set a “refresh duration”, it is the time interval (in minutes, but can be customized in seconds, see comments in the code) between two refreshes of the moon position (access to the azel.dat file in WSJT-X).

Rotator Controller 4

Every x (2 in the example) minutes, as per defined by the “refresh duration”, the moon position is derived from the azel.dat file, the antenna is rotated and elevated accoringly, and the actual antenna position (azimuth and elevation) is reported 5 seconds later.

Rotator Controller 5

If the moon is below the horizon, nothing happens and the program indicates “The moon is below horizon, antenna parked !”, and so on until the moon rises above the horizon. To  exit, press “CTRL” and “C”.

Rotator Controller 6

Here is the heavily commented python 3 program :

# coding: utf-8

# This script allows to rotate an antenna system either by manually typing in a heading (azimuth and/or elevation)
# you want the antenna to rotate to or by automatically tracking the moon azimuth and elevation.
# The Moon azimuth and elevation are extracted out of the file azel.dat provided by the WSJT-X suite.
# Beside WSJT-X, this script also makes use of the rotator controller daemon embedded in the Hamlib
# libraries. So, the prerequisites for this script to run are WSJT-X opened and running, and the
# Hamlib librairies installed on your Raspberry Pi.
# This script has been written in Python 3 by Didier, ON4KDV and Gaëtan, ON4KHG.
# My personnal system makes use of a Raspberry Pi 4B and an ERC-3D (DF9GR) antenna rotator controller.
# Both are linked with a USB (on the Raspberry Pi side) to RS232 serial (on the controller side) connection.
# First, import the time, system and subprocess functions needed in this script

import time
import os, sys
import subprocess

# Select the USB port of the Raspberry Pi onto which the rotator controller is connected.
# Normally, as WSJT-X is started beforehand, the USB0 port is already assigned for the CAT & PTT of WSJT-X.
# In my case, I usually select USB1, so, I type “1” below. 

usb_port=(input(“USB port to use (0-3) : “))

if usb_port == “0”:

# The Rotctld(aemon) is opened with the command “rotctld -m 601 -r /dev/ttyUSB0 &”.
# -m 601 is related to the GS-232A rotator protocol.
# You could have to have to select another number according to the protocol and associated hardware in use on your side.
# The list of supported protocols can be obtained by typing “rotctld -l” in the terminal.
# ttyUSB0 is the serial port (USB0) of the Raspberry onto which the rotator controller is connected.‘rotctld -m 601 -r /dev/ttyUSB0 &’, shell=True)

# And so on for the other USB ports.
elif usb_port == “1”:
        ‘rotctld -m 601 -r /dev/ttyUSB1 &’, shell=True)
elif usb_port == “2”:
        ‘rotctld -m 601 -r /dev/ttyUSB2 &’, shell=True)
elif usb_port == “3”:
        ‘rotctld -m 601 -r /dev/ttyUSB3 &’, shell=True)
# Select the working mode : manual, automatic (moon tracking) or exit.

Mode=(input(“MANUAL rotation/elevation, type m\nAUTOMATIC tracking, type a\nEXIT, type e\n\n”))

if Mode == “m”:

# If “m” is chosen, the manual rotation mode is selected.
    while (True):

# Then, select what you want to do : rotate/elevate the antenna, get its current position, stop the rotation in case
# of emergency or exit.
      Submode=(input(“\nROTATE antenna (azimuth only) and set elevation to 0°, type r\nROTATE and ELEVATE antenna (azimuth and elevation), type l\nGet current POSITION, type p\nSTOP, type s\nEXIT, type e\n\n”))
      if Submode == “r”:
# If “r” is selected, instruct towards which azimuth the antenna has to rotate. With this selection, if the antenna was previously elevated,
# it will be set back to 0° (no elevation) too.        
         az=(input(“\nRotate antenna to azimuth (°) : “))
# Build and execute the command “set_pos”. The default port used by the rotctld(aemon) is 4533.

         command_az=’echo “|\set_pos ‘ + az + ‘ 0” | nc -w 1 localhost 4533’

      elif Submode == “l”:

# If “l” is selected, you can set whatever azimuth and elevation angle.

         az=(input(“\nRotate antenna to azimuth (°) : “))
         el=(input(“Elevate antenna to elevation (°) : “))

         command_azel=’echo “|\set_pos ‘ + az + ‘ ‘ + el + ‘” | nc -w 1 localhost 4533’
# If “p” to get the position of the antenne is selected, build and execute the command “get_pos”.     
      elif Submode == “p”:
         command_azpos=’echo “|\get_pos” | nc -w 1 localhost 4533′

# If “s” to (emergency) stop is selected, build and execute the command “S” (Stop).

      elif Submode == “s”:
         command_stop=’echo S | nc -w 1 localhost 4533′

# If none of the above is selected, then exit the script.

elif Mode == “a”:

# If “a” is chosen, the automatic (moon tracking) mode is selected.
# Set the refresh rate in minutes of the moon azimuth and elevation.
# With narrow beamwidth antennas (microwaves), the refresh rate has to be fast.
# With wide beamwidth antennas, the refresh rate can be slower. On 2m, I chose 4 minutes.
# If you want a refresh time in seconds, remove the line “ref=ref*60” and rename “min” into “sec”
# in the line ref=int(input(“\nRefresh duration (min) : “)).
    ref=int(input(“\nRefresh duration (min) : “))
    while (True):

# Open the file azel.dat of WSJT-X in read mode. Make sure the path “/home/pi/.local/share/WSJT-X/azel.dat” is the same
# than the path defined in the settings of WSJT-X (see the WSJT-X user guide).
# The first line of the file is read and placed in the variable “txt” and then the file is closed.


      if (“Moon” in txt):                  # Check that the first line contains the word “Moon”. 
        p=txt.find(“,”)                    # Search for the 1st comma (,).
        if (p > 1):                        # If the 1st comma has been found
            txt=txt[p+1:]                  # what is in front of the 1st comma is removed, including the comma.
            p=txt.find(“,”)                # Search for the 2nd comma.
            if (p > 1):                    # If the 2nd comma has been found

                az=txt[0:p]              # the text in between the 1st and 2nd commas is saved in “az” (azimuth).
                az=az.strip()            # Spaces before and after are removed, only the figures/sign are kept.
                print(“Moon azimuth (°) :”,az)   # The moon azimuth is displayed.
                txt=txt[p+1:]              # What is in front of the 2nd comma is removed, including the comma.
                p=txt.find(“,”)          # Search for the 3rd comma.
                if (p > 1):              # If the 3rd comma has been found

                    el=txt[0:p]          # the text in between the 2nd and 3rd commas is saved in “el” (elevation).
                    el=el.strip()        # Spaces before and after are removed, only the figures/sign are kept.
                    print(“Moon elevation (°) :”,el)   # The moon elevation is displayed.

# Build the command “set_pos” as from the az and el variables extracted above.

                    command_azel=’echo “|\set_pos ‘ + az + ‘ ‘ + el + ‘” | nc -w 1 localhost 4533’
# Convert az and el from text to float (numerical) format.


# Execute the command only if the azimuth is between 0 and 360° and if the elevation is between 0 and 90°.

                    if(f_az >0 and f_az <361 and f_el >0 and f_el <91):
# Otherwise the moon is below the horizon and nothing is executed but displaying that the moon is below horizon.

                         print(‘\nThe moon is below horizon, antenna parked !’)
# We wait 5 seconds, the time for the antenna to rotate (it may actually take longer, depending on the position of
# the antenna at startup of the tracking) and then we display the position of the antenna.
# Finally, we indicate that we wait until the next update, according to the refresh duration (ref) defined above.
                    command_azelpos=’echo “|\get_pos” | nc -w 1 localhost 4533′
                    print(‘\nWaiting for the next update or CTRL+C to exit\n’)

# If none of the working mode (manual or automatic moon tracking) was selected, exit and close the script.




Posted in Antennes / Antennas | Comments Off on Antenna rotator control program for Raspberry Pi and WSJT-X

Geminids / Géminides 2020


Ci-dessous quelques cartes des stations entendues durant les Géminides 2020 sur 144 MHz, des stations qui m’ont entendu et des stations que j’ai contactées.

Below are some maps of the stations heard during the 2020 Geminids on 144 MHz, the stations that heard me and the stations that I worked.

QRA locators entendus (Tropo ou MS) / QRA locators heard (Tropo or MS) :

Locators heard by ON4KHG - Geminids 2020

Stations entendues (Tropo ou MS) / Stations heard (Tropo or MS) :

Stations heard by ON4KHG - Geminids 2020

QRA locators où j’ai été entendu (Tropo ou MS) / QRA locators where I have been heard (Tropo or MS) :

Locators where ON4KHG has been heard - Geminids 2020

Stations qui m’ont entendu (Tropo ou MS), la plus lointaine RQ7R en KN64SO = 2305 km / Stations that have heard me (Tropo or MS), the furthest RQ7R in KN64SO = 2305 km :

Stations where ON4KHG has been heard - Geminids 2020

Stations contactées (MS) / Stations worked (MS) :

Log ON4KHG Geminids 2020



Posted in Trafic / Traffic | Comments Off on Geminids / Géminides 2020

144 MHz Tropo opening September 9th, 2020 / ouverture Tropo 9 septembre 2020


Le 9 septembre 2020, nous avons été gratifiés d’une belle ouverture tropo sur 144 MHz. Depuis JO10, le conduit (“duct”) permettait des QSO jusqu’en SP et l’ouest de la Biélorussie, mais aussi et surtout jusque dans l’est de l’Ukraine, via ce qui semblait être un second conduit jumelé au précédent.
Mon log se trouve ci-dessous. Tous les QSO’s ont été réalisés en FT8, seuls certains vers SP auraient pu être réalisés en CW.
Deux QSO à plus de 2000 km, avec US8AR (2065 km) et UY0LL (2264 km). Bien que nous nous soyons “entendus” (plutôt vus) mutuellement, le QSO avec UR7IMM (2410 km) n’a pu être compété.

On September 9, 2020, we were gratified with a beautiful tropo opening on 144 MHz. Since JO10, the duct allowed QSO’s up to SP and the west of Belarus, but also and above all up to the east of Ukraine, via what seemed to be a second duct twinned with the previous one.
My log is below. All the QSO’s were made in FT8, only some to SP could have been made in CW.
Two QSO’s at more than 2000 km, with US8AR (2065 km) and UY0LL (2264 km). Although we “heard” (rather saw) each other, the QSO with UR7IMM (2410 km) could not be completed.

Tropo 09092020

Ci-dessous le log / Below the log :

UTC Callsign QRG Mode Sent Rcved Locator
05:07 EU3CZ 144,174 FT8 -15 -04 KO12UB
05:11 SP6ECQ 144,174 FT8 -12 +01 JO71SD
05:14 SP1MVG 144,174 FT8 -08 -01 JO74JA
05:30 SP1FJZ 144,174 FT8 -03 +08 JO84EE
05:34 US8AR 144,174 FT8 -20 -13 KO60RR
05:38 SP6URZ 144,174 FT8 -10 -13 JO71MD
05:42 SP7CKH 144,174 FT8 -15 -08 JO92QF
05:47 EU3AI 144,174 FT8 -19 -02 KO22CE
06:09 UY0LL 144,174 FT8 -12 -01 KN79XX
06:12 SO3Z 144,174 FT8 -07 -02 JO82LJ
06:31 SQ5ESM 144,174 FT8 -16 -14 KO02HC
06:33 EW3AA 144,174 FT8 -19 -04 KO12TC
07:40 SM7MBH 144,174 FT8 -14 +02 JO75EM
07:43 SP7MTU 144,174 FT8 -16 -14 JO91RR
08:02 SQ2SAT 144,174 FT8 -16 -10 JO83XG

Extrait de PSK Reporter qui montre les stations qui ont reçu ON4KHG (et qui le rapportent sur PSK Reporter) :

Extract of PSK Reporter showing the stations that have received ON4KHG (and are reporting it to PSK Reporter) :

Display Reception Reports - Google Chrome_2020-09-09_10-06-04

Extrait de PSK Reporter qui montre les stations reçues par ON4KHG :

Extract of PSK Reporter showing the stations received by ON4KHG :

Display Reception Reports - Google Chrome_2020-09-09_10-07-43

Capture d’écran de WSJT-X montrant le QSO avec UY0LL :

Screenshot of WSJT-X showing the QSO with UY0LL :

WSJT-X v2.2


Posted in Trafic / Traffic | Comments Off on 144 MHz Tropo opening September 9th, 2020 / ouverture Tropo 9 septembre 2020

UT1FG/MM (Yuri) – 144 MHz


 Update dating 23/11/2020 (see also original post of 06/01/2019 below)

These last weeks, Yuri was again on the seas !
Thanks to the kind support of a few PA and DL hams, Yuri operates now with 150W on 144 MHz. The whole VHF DX community should be grateful for that support ! It allows every interested ham to work even more new “wet” squares !

Looking back at all the wet squares I have worked so far on 2m thanks to the operations of Yuri, it looks like this (Tropo and MS) :

Wked UT1FG-MM 27112020
However, the antenna used by Yuri is a “tiny” 5 elements that holds on a 80 cm long boom, as you can see on the pictures below (click on the pictures to enlarge). This is a dual band VHF/UHF antenna with a gain of 5  dBi on 144 MHz.

It is amazing to see how such a setup can produce FB MS reflections at 2000 km+, and how the tropo coverage extends far away too. In this regard, the uncluttered and flat surroundings, the sea, is quite helpful…

A 25 sec MS reflection (mode FSK441) from UT1FG/MM received by Bernd, DF2ZC (JO30RN) at +/- 2000 km  :

A 2 sec one received here at my station when Yuri was in HN83XL (2112 km far away) :


…But the sea is not always that flat !

Yuri at its operating position : WhatsApp Image 2020-11-17 at 13.44.24



Thanks again to Yuri and the supportive team behind him. Well done !



Post of 06/01/2019 

Yuri, UT1FG is often active as UT1FG/MM on 144 MHz. He operates from the ship “Goldeneye”, a bulk carrier sailing under Cyprus flag. On the page of Yuri, one can see the ship.

2019_01_06_21_02_48_https_s3.amazonaws.com_files.qrz.com_g_ut1fg_IMG_1476453682005_V.jpg_InterneThe Goldeneye has left San Lorenzo in Argentina on 8/12/2018 with Riga (Latvia) as destination. Yuri has been active during the travel from several “wet” squares on 144 MHz. I have been lucky enough to work him when he was in IN66, JO12, JO25 and JO36. Three of them were new #. The mode Yuri used was FT8. One likes FT8 or not is another discussion ! I wanted to know the full (6 digits) loactors where Yuri was when I worked him. Provided the latitude / longitude position of the ship can be received by costal stations receiving AIS (Automatic Identification System) frames emitted by the boats (161.975 and 162.025 MHz), it is quite easy to derive the QRA locator. I describe here the way I proceeded.

I go the either or One has sometimes a more recent position that the other or vice-versa. The AIS frames contain the latitude and longitude of the ship ; these are broadcasted on the above mentioned frequencies. Once received by a costal AIS station that reports the received postions of the ships to vesselfinder or marinetraffic over the internet, one can retrieve the latitude and longitude of the ship. Below is an example with marinetraffic. Look for “Goldeneye” on the top right when accessing the website and you will get this if you select the Goldeneye from Cyprus, i.e. [CY] (click on picture to enlarge) :


From there, you can see the latest postion of the ship, its past track and even its forecasted route.


On the top right of the picture above, one can see the latitude and longitude of the ship, expressed both in DD and DMS. Feeding an online QRA locator calculator2019_01_03_09_39_15_QTH_locator_calculator (e.g. with these data and one get the QRA locator of the ship. In the present case, it was in JO24KH. Again, this is possible provided the ship is under the coverage of a costal AIS receiving station. Otherwise, satellite positioning of the ships is also possible but this is a paying service !

End of January 2019, on his way from Estonia to Brazil, Yuri has activated again wet squares on 144 MHz. Yuri is transmitting with 20W into a 5 elements antenna. Hereby a screenshot from JO35 :

UT1FG-MM JO35 30012019
Many thanks to Yuri for activating all these wet squares !


Posted in Trafic / Traffic | Tagged | Comments Off on UT1FG/MM (Yuri) – 144 MHz

Marconi Memorial VHF CW Contest 2020


Compte rendu du contest MMC VHF des 7 et 8 novembre 2020 ; il s’agit du seul contest VHF dédié uniquement à la télégraphie (CW), et un de mes préférés ! Preuve qu’on peut être un amateur “moderne” en faisant usage des modes numériques (FT8, JT65,…), tout en restant un fervent pratiquant de télégraphie, pourtant si archaïque et démodée, mais qui me plait toujours autant ! La tolérance et l’ouverture d’esprit sont toujours plus constructives que les clivages, surtout pour ce qui n’est finalement qu’un hobby…
Pour une fois, j’avais décidé de participer sérieusement à ce contest. A l’exception donc d’une petite nuit de sommeil et de quelques breaks, j’ai été actif durant environ 18 heures. Dans certains pays, la crise covid-19 interdisait la tenue des activités radio en portable et/ou les ressemblements. De fait, moins de stations étaient actives. Malgré ça, ce fût un contest très plaisant, même si le début s’est déroulé en mode “mineur”, avec peu de DX’s. Il a fallu attendre 16h27 UTC pour faire le premier QSO à plus de 800 km (F4CWN / JN03). Ensuite, les conditions se sont améliorées en soirée et durant la nuit, durant laquelle plusieurs DX furent contactés. Notamment le meilleur DX (OM3W / JN99) à 1027 km a été contacté en total random, sans prise de sked sur “KST”. 9A1P en JN65 (941 km) a également été contacté. Côté score, avec mes 2×9 él. mises côte-à-côte (angle d’ouverture étroit dans le plan horizontal), je ne peux rivaliser avec les stations qui possèdent plusieurs systèmes d’antennes à ouverture horizontale large. Je suis toutefois très satisfait du résultat et du taux de réponse à mes CQ.
Station utilisée : 2×9 él. DK7ZB et 1,2kW

Activity report of the MMC VHF contest of November 7th and 8th, 2020; it is the only VHF contest dedicated solely to telegraphy (CW), and one of my favourite ones ! Proof that one can be a “modern” ham using digital modes (FT8, JT65,…), while remaining a fervent practitioner of telegraphy, which is so archaic and old-fashioned, but which still pleases me so much ! Tolerance and open-mindedness are always more constructive than cleavages, especially for what is actually just a hobby…
For once, I had decided to participate seriously in this contest. So, apart from a short night’s sleep and a few breaks, I was active for about 18 hours. In some countries, the covid-19 crisis had forbidden portable radio activities and/or gatherings of people. Indeed, fewer stations were active. In spite of this, it was a very pleasant contest, even if the beginning was in “minor” mode, with few DX’s. I had to wait until 16h27 UTC to work the first QSO at over 800 km (F4CWN / JN03). Then conditions improved in the evening and during the night, during which several DX’s were contacted. Notably the best DX (OM3W / JN99) at 1027 km was contacted in total random, without taking a sked on “KST”. 9A1P in JN65 (941 km) was also contacted. Regarding the score, with my 2×9 el. bayed side by side (narrow aperture angle in the horizontal plane), I can’t compete with stations that have several antenna systems with wide horizontal apertures. However, I am very satisfied with the result and the response rate to my CQ’s.
Station used : 2×9 el. DK7ZB and 1,2kW

# QSO’s : 228
Points : 93903
# DXCC : 15 (G, GW, HB9, OE, OK, OM, DL, ON, PA, F, I, SM, OZ, GM, 9A)
# WWL : 76
Average km/QSO : 412

Top 10 DX QSO’s :

OM3W           JN99CH      1027 km 
9A1P           JN65VG       941 km
OK1MZM         JN89IW       908 km
F2CT           IN93GJ       900 km 
OK2R           JN89DO       885 km 
OK7W           JO80FG       885 km 
IK4PMB         JN54MM       859 km 
GM4YXI         IO87WK       856 km 
7S7V           JO65SN       843 km 
OK2KGB         JN79QJ       826 km

MMC 2020 November

Quelques fichiers audio ci-dessous / A few audio files below.

OM3W / JN99CH / 1027 km :

OK7W / JO80FG / 885 km :

OK1TEH / JO70FD / 746 km :

OK1MZM / JN89IW / 908 km :

IK4PMB / JN54MM / 859 km :

F2CT / IN93GJ / 900 km :



Posted in Concours / Contests | Tagged , | Comments Off on Marconi Memorial VHF CW Contest 2020