Tuesday, 31 March 2026

50 Experimenting with Python and TKinter talking to an Arduino

 We have a need to send two numbers to a remote Arduino and receive a complex text string from its GPS.

I decided to use the arrow keys on the keyboard, the right and left arrows increment and decrement one of the numbers from 0 to 99 (might change). The up and down arrow keys increment and decrement the other number. These will eventually control Rudder and sail position on a model yacht, using two servos connected to an arduino on the boat. It is connected to a RFM69HCW radio transceiver (obtained from Pimoroin.co.uk, it is sold by Adafruit in the states). A PC on the shore or support boat is wired to another arduino and that arduino also has an RFM69HCW radio transceiver. The boat arduino also has a GPS module. Future extensions will add wind sensors etc.,

To get a very basic working system I decided to use the Tkinter graphics system that is supplied with python. This allows a basic crude graphic user interface (GUI) that looks dated now. There are extensions to Tkinter and there are other graphic libraries available, but I decided to keep it simple. 

In addition there are a number of ways to put graphical objects (known as widgets) on a main window. You can specify a .pack method or a .grid method. Most books will guide you through these, or just copy code and experiment. However, I used a much simpler system that uses a .place method.

The .place method is passed an x and a y value and the widget is simply placed there. This works fine until you try to resize the main window. It is also a bit tedious as you have to plan and work out where you want everything to line, I do not consider that onerous. And I don't need resizable windows.

Before presenting code let me say that I am cherry picking bits of code and ignoring others, I use a minimum of widgets, just enough to get the job done, it could be prettier.

I only use one window, I call it master, it could have been named anything, some books call the first window root. It is possible to create a second window totally within the master window, the frame widget allows this. We may need that later. I use a pulldown widget to set the comms port. I use labels and I use the .bind method to attach a function to the keys that I want to trigger an action.

That should make the code make some sense!

Also of note is that Tkinter (and most of the other GUI systems) use a different way of working from simple code. It is event driven programming. You are used to simple code where the execution flows down the page, one statement after another, with the odd loop and jump to control the flow of control.

TKinter programs initially look a bit funny, the code moves down the page but then there is a final function call. In traditional programming terms this function is entered and NEVER leaves. You might think the code would just freeze but what actually happens is that any widget defined before that final call has a number of methods attached to it and the final call creates a list of possible events and as each event happens (such as you clicking a mouse or hitting a key) that final function calls the appropriate method. After it returns you are back in that final function again. It is running an event scheduler. You can supply your own functions and these get called is they are defined as callback functions by the appropriate widget. It will make more sense once you run the program.

A further complication is that the widgets can use variables of several types but these are derived from the normal types of variable. So, whilst we might set the text attribute of a widget to some fixed text, if we want to have a string that changes we have to (a) use the textvariable attribute, not the text one. and (b) we have to use a type known as stringvar, not string. We assign values to stringvar variables using the .set(value) method and we read values out of stringvars using the .get() method. Watch out for this as it took me a while to realise this. We can trigger a function if a stringvar changes which may be useful later (I write this blog post before the project code is complete...)

Here is a demo Python/Tkinter program; (watch out for correct indenting...)

from tkinter import *

def keypress(event): # we bind the arrow keys, rest ignored here

    """Receive a keypress and move the Servos by a specified amount"""

    if event.keysym == 'Up':

        Sail.set(Sail.get()+1)

        if Sail.get() == 100:

            Sail.set(99)

    elif event.keysym == 'Down':

        Sail.set(Sail.get()-1)

        if Sail.get() < 0:

            Sail.set(0)        

    elif event.keysym == 'Right':

        Rudder.set(Rudder.get()+1)

        if Rudder.get() == 100:

            Rudder.set(99)  

    elif event.keysym == 'Left':

        Rudder.set(Rudder.get()-1)

        if Rudder.get() < 0:

            Rudder.set(0)

    else:

        pass


##########################################################

master = Tk() # every TKinter program uses this to create the first window

master.geometry("600x200") # here we use the .geometry window to set size


# Dropdown options  

Ports = [ "COM3:","COM4:","COM5:","COM6:"

         ,"COM7:","COM8:","COM9:","COM10:"

         ,"COM11:","COM12:","COM13:","COM14:"]  

# Selected option variable  

Port = StringVar()  


# Dropdown menu  

OptionMenu(master, Port, *Ports).place(x=1,y=1)  # place puts it in the window


# setup rudder and sail controls to arrow keys

Rudder = IntVar(master,50)

Sail = IntVar(master,60)


master.bind('<Up>',keypress)

master.bind('<Down>',keypress)

master.bind('<Left>',keypress)

master.bind('<Right>',keypress)


Label(master, text = "Rudder Position = ").place(x=25,y=40)

Label(master, textvariable = Rudder).place(x=125, y=40)


Label(master, text = "Sail Position = ").place(x=225,y=40)

Label(master, textvariable = Sail).place(x=305, y=40)


# could paint graphic of Rudder and Sail positions here...

#

#



# Got Comm port and rudder and sail position, send them...

# every time they change.

# and receive GPS position into a stringvar (so Tk updates them)

#


mainloop() # infinite loop needed, stringvars & widgets update... 


49 Arduino driving RFM69HCW radio transceivers

 I recently bought a couple of radio transceivers to allow slow speed transfer of data between two Arduinos. I used the RFM69HCW $10 modules from Adafruit. These do not use the LoRa protocol but a simpler packet protocol that suited my application. Point to Point.

Here in the UK I bought them from https://shop.pimoroni.com/products/adafruit-rfm69hcw-transceiver-radio-breakout?variant=19594984071 at just under a tenner, I choose the 433MHz versions as it is easier to make antennas for 70cms and I have test equipment that allows tuning such antennas.

A declared power of 100mW and range of 500m sounded good. The data sheet says 50mA (+13 dBm) to 150mA (+20dBm) current draw for transmissions, ~30mA during active radio listening. The range figure is for line of sight and depends on the antennas. My application will have a PC linked to an Arduino on shore and a mobile model boat on water, the shore station can use a Yagi or decent antenna even if the boat only uses a simple quarter wave whip.

The tutorial on the Adafruit website shows it wired to their own metro boards, I had a couple of Seeduino Nano modules and used them. Their +5 volt output can source 200mA and can power the module directly. There are Arduinos that have limited power supply outputs from the internal 5 volt regulator so you might need to power the transceivers separately in some applications.

There are two Arduino libraries that support these modules, one from LowPowerLabs and one from RadioHead. I had a lot of bother initially when I was using the LowPowerLabs libraries but have since concluded it was probably not the fault of the library. I moved on to the RadioHead Libraries as Adafruit had a modifed version in their Github account. 

Their tutorial is at https://learn.adafruit.com/adafruit-rfm69hcw-and-rfm96-rfm95-rfm98-lora-packet-padio-breakouts and the software they recommend is at https://github.com/adafruit/RadioHead 

Be very careful! the current RadioHead library at their site is version 1.145 and the Arduino IDE keeps wanting to update your IDE to this version. RESIST THE TEMPTATION TO UPDATE THE LIBRARY when a pop-up appears in your IDE. The AdaFruit version is based on version 1.121.

The Adafruit has many more examples than the up to date version, use the Adafruit version and DO NOT UPDATE it. Or at least, I know if you use the Adafruit one the code below will work, by all means, once you have a working system, change libraries, test it and let me know. I just wanted to get a basic working system and then move on (to write a Python Tkinter program to interface to the arduinos)

The Adafruit library can be downloaded as a .ZIP file from their github account. You can easily install a Zipped library into your Arduino IDE installation by copying it to your libraries folder. The Libraries folder is in your Sketch folder. By default in your Documents folder although in my installation I have moved mine to a folder on the desktop, if you move your sketch folder the libraries folder moves with it.

Place your downloaded .zip into the libraries folder and start the Arduino IDE, select the top line menu item "Sketch"->Include Library>Add  .Zip Library. Do not use the new built in library manager icon on the left of the IDE.

I initially used the simple examples you get when you install the library but couldn't get them working, I tried several different wirings and then started to look at other examples. Some had code to pulse the reset line, some examples didn't even wire up the reset pin on the module. I suspect that on some Arduinos the i/o lines come up with a rise time that allows the module to reset itself on powerup. In any case by modifying the "feather arduino" example and adding reset code and wiring I was able to get it going. YMMV.

Here is my wiring;

As you can see the SCK,MOSI and MISO pins goto the Arduino SPI pins. The CS select pin and G0 pin (interrrupt) go to suitable digital pins and are specified when you create the RH_RF69 object from the RF69 class. The RST reset pin is just wired to a digital pin that you make an output. 

I stripped the code down to the minimum and it is listed below. 

Note the "tx" code calls .send and then .waitPacketSent before using .waitAvailableTimeout to wait for data which is picked up with a .recv call. Just for interest I pickup the Received Signal Strength Indication (RSSI) to see how well the antennas are working and range tests (yet to done). The method .lastRssi returns a number in dB (I think). Setup uses the methods .init, .setFrequency, .setTxPower and .setEncryption

Note the receive code has similar setup code and its loop section calls .available to see if data has arrived. The .recv method gets the data into a buffer and I, again, read the signal strength of the last received packet with .lastRssi. A quick .send and .waitPacketSent gives a handshake back to the transmitter code to repeat the cycle

If you look into the .zip library and read the .h and .cpp files you can see the documentation, or at least a list of the various methods (functions) available to you.

// From Feather RawDemo Tx

#include <SPI.h>
#include <RH_RF69.h>  //Version 1.121 has more examples than later, don't update for now!!!
#define RF69_FREQ 434.0
#define RFM69_CS     10  // "B"
#define RFM69_INT    2 // "C"
#define RFM69_RST    6  // "A"
uint8_t rudder_position=49;// pick up new values from PC
uint8_t sail_position=48;
RH_RF69 rf69(RFM69_CS, RFM69_INT); // create Object
void setup() {
  Serial.begin(115200);
  pinMode(RFM69_RST, OUTPUT);
  digitalWrite(RFM69_RST, LOW);delay(10);// slow reset
  digitalWrite(RFM69_RST, HIGH);delay(10);
  digitalWrite(RFM69_RST, LOW);delay(10);
  if (!rf69.init()) {Serial.println("RFM69 radio init failed");while (1);}//hang
  Serial.println("RFM69 radio init OK!");
  if (!rf69.setFrequency(RF69_FREQ)) {Serial.println("setFrequency failed");}
  rf69.setTxPower(20, true);// 2nd argument must be true for RFM69HCW
 
  // The encryption key has to be the same as the one in the server
  uint8_t key[] = { 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02,
                    0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02};//IMcC chose..
  rf69.setEncryptionKey(key);
}
void loop() {
  delay(2000);// don't flood the receiver, could make bigger...
  char radiopacket[7] = "r00s00";// Rudder and sail position
  // Pick up variables from PC here, test with 00 <<<<<
  rudder_position=51;// test
  sail_position=61;// test
  itoa(rudder_position, radiopacket+1, 10);// overwrite first 00, base 10
  itoa(sail_position, radiopacket+4, 10);// overwrite 2nd 00, base 10
  Serial.println(radiopacket);
  // Send a message!
  rf69.send((uint8_t *)radiopacket, strlen(radiopacket));
  rf69.waitPacketSent();
  // Now wait for a reply
  uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];
  uint8_t len = sizeof(buf);
  if (rf69.waitAvailableTimeout(500)) {
    // Should be a reply message for us now
    if (rf69.recv(buf, &len)) {
      Serial.println((char*)buf);
      // Should be GPS position, pass on up to PC
      Serial.print("RSSI: ");Serial.println(rf69.lastRssi(), DEC);
    } else {
      Serial.println("Receive failed");
    }
  } else {
    Serial.println("No reply");
  }
}
Here also is the code for a suitable receiver

// From Feather RawDemo RX
#include <SPI.h>
#include <RH_RF69.h> //Version 1.121 has more examples than later, don't update for now!!!

#define RF69_FREQ 434.0
#define RFM69_CS     10
#define RFM69_INT    2
#define RFM69_RST    6

uint8_t rudder_position=49;
uint8_t sail_position=48;
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);

RH_RF69 rf69(RFM69_CS, RFM69_INT); // create object

void setup() {
  Serial.begin(115200);

  pinMode(RFM69_RST, OUTPUT);
  digitalWrite(RFM69_RST, LOW);delay(10);// slow reset
  digitalWrite(RFM69_RST, HIGH);delay(10);
  digitalWrite(RFM69_RST, LOW);delay(10);

  if (!rf69.init()) {Serial.println("RFM69 radio init failed"); while (1);}// hang
  Serial.println("RFM69 radio init OK!");
  if (!rf69.setFrequency(RF69_FREQ)) {Serial.println("setFrequency failed");}
  rf69.setTxPower(20, true);// 2nd argument must be true for RFM69HCW

  // The encryption key has to be the same as the one in the server
  uint8_t key[] = { 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02,
                    0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02};//IMcC chose..
  rf69.setEncryptionKey(key);
}

void loop() {
 if (rf69.available()) {
    // Should be a message for us now
    if (rf69.recv(buf, &len)) {
      if (!len) return;// if zero no data
      buf[len] = 0;// terminate string
      rudder_position=(buf[2]-'\0')*10+(buf[2]-'\0');
      sail_position=(buf[4]-'\0')*10+(buf[5]-'\0');
      // now have two decimal numbers 0 to 99
      // how do you print numbers in the arduino IDE?



      Serial.print("RSSI: ");Serial.println(rf69.lastRssi(), DEC);
      // call function here to get position, course & speed<<<<
      strcpy(buf, "55.1234N,005.4321W,045,12");
      rf69.send(buf, sizeof(buf));
      rf69.waitPacketSent();
      Serial.println("Sent a reply");
    } else {
      Serial.println("Receive failed");
    }
  }// fi .available
}//--end loop--//


------------ ends as of March 2026 -----------------




Monday, 11 August 2025

48 - How hot does a heat sink get?

When designing electronic circuits you need to take into account the temperature of the components, if there is too much heat they will fail, if they run hot their lifetime is reduced. I once did reliability calculations for a friend to show how how much a temperature of 95 degrees Celsius reduced the lifetime of a complete electronic system, the notional "Mean Time To Failure" (MTTR) reduces exponentially as temperature goes up, the Arrhenius expression gives an indication of this, but it is not the complete story.

 A common rule of thumb is that for every 10°C increase in temperature, the failure rate of a component doubles, but this is conservative as it does not take into account all the reasons for failure of electronic systems. but even so, clearly going from 25 degrees to 55 degrees causes failure at least  2^3 or 8 times quicker.

Temperatures above 50 degrees should be avoided, 50 is about the maximum you can put your finger on (for about 5 seconds) without too much "ouch". A modern thermal imager is a useful tool. It is only accurate if the emmissivity of what is being looked at it is not too extreme.

The traditional way to calculate the temperatures is to use thermal resistance figures and add these up - the same way you add resistors in series. For semiconductors (the most sensitive of our components I think, at least for rapid failure, some capacitors do dry out at elevated temperatures) you want the temperature inside the transistor or diode to be kept below the manufacturers maximum and you increase lifetime if you can get well below it. The manufacturer hopefully publishes the thermal resistance from the semiconductor junction to the case. If this is all that is used them you can calculate what temperature the junction is if the case is at a certain value and the device is dissipating so many watts of power. Thermal resistances are the most convenient to work with, some datasheets give the data in different ways, derating curves or values, you can convert these to thermal resistances or, at worst, use data for the same physical package using a datasheet for a similar device

For example: the datasheet for the IRF510 states thermal resistance ratings as;

    Junction-to-ambient RthJA as 62 degrees C per watt  (this is for when no heatsink is used)

    Junction-to-case RthJC of 3.5 degrees/watt. (used when the case is not just in air)

    Case-to-(Heat)sink, flat greased surface RthCS of 0.5 degrees per watt 

(Note you can grease the interface, insert mica washers for electrical insulation but good heat transfer or use thermal pads, all have values well under a half a degree per watt)

An example heatsink costing £3 to £4 and two inches high, an inch and half wide and half an inch deep has a specified natural (non fan assisted) thermal resistance of 9 degrees C per watt (FEC1892328). For around a tenner you can get 3 or 4 degrees per watt heatsinks. Bigger is expensive.

The device has a maximum junction temperature of 175 degrees C (though I'd prefer to opt for 125)

The temperature rise above ambient is simply the power times the thermal resistance

Thus if the device is running at 10 Watts and we assume an ambient temperature of 25 degrees we have;

              Pd * (Rjc + Rcs + Rsa) + Ta  =  Tj  

              = 10 * (3.5 + 0.5 + 9 ) + 25   =  155  ignoring dissipation from case directly to air.

Fairly hot! and if the heatsink is inside a case that is poorly ventilated the "ambient" can easily be tens of degrees above 25. A fan or bigger heatsink is needed.

Big heat sinks are expensive, Harry Lythall (SM0VPO) has good rules of thumb on his website suggesting that if you take the square area of aluminium exposed to air then the thermal resistance of home made heat sinks is roughly 


Taken from http://sm0vpo.altervista.org/begin/heat-0.htm 

We assumed constant power being dissipated in the example above, so you'd have to use the maximum power as the worst case, if the device was being switched and was only "on" 10% of the time then you could guesstimate that the effective power was a tenth of the peak, but only if the cycle time was fast.

If the device is passing an SSB modulated carrier then a "duty cycle of 30% might be appropriate, which drops to 15% if you only transmit half the time. This assumes you don't talk for too long before saying "over". A large transmitter heatsink and case does have significant thermal mass which helps.

The ARRL says; (ignoring the ratio of receive to transmit times)

Conversational SSB with no speech processing, uses a 20% duty cycle which includes voice characteristics and syllabic duty factor.

Conversational SSB with heavy speech processing, uses a 50% duty cycle which includes voice characteristics and syllabic duty factor.

Conversational CW, uses a 40% duty cycle

Voice FM, RTTY, FT8 use a 100% duty cycle.

--- 

What we don't know from the simple "heat resistance" model are the time constants involved,

How quick do things heat up and cool down?

I never learnt this at University, never saw it discussed in my textbooks and when I asked a couple of mechanical engineering university lecturers about it, they didn't know either, Other than a cursory mention of thermal mass and their specific capacities. I put the calculation to one side as the one time I needed it I did a crude calculation and actually measured a time graph of the temperature rise, close enough in my specific case. I always thought that I must get back to that and research how to do it "properly".

Then along came a Youtube video by "FesZ",  Riccardo Tinivella entitled "Static and Transient Thermal Models", you can find it at  https://youtu.be/BGi_n28D8ro?si=CAK3EodXQ0U-wn-K 

For the simple static cases of above, FesZ uses resistors and adds a voltage source to model ambient temperature and a current source to represent the source of energy (heat). Voltage is an analogy for temperature and the amps represents watts in the current source.

Even this "model" is useful if there are several sources of heat, the "circuit" then has parallel and series resistance between the several current sources and the one voltage source. You can see what each device case temperature gets to. LTspice does the calculation for you. 

For the single device described above I made three models.

First I modelled the equation above and then added the case to air dissipation (R8), I then adjusted the given 62 degrees/watt to twice this value, reasoning that only half the case is exposed to the air and half connected to the heatsink.


So junction temperatures ranged from 155, 133 and 143 degrees for 10 Watts of dissipation, 100% of the time.

If we had two IRF510s, each dissipating 5 watts then the circuit, and corresponding temperatures would be as below;  the junctions are now at just under 120 degrees. The heatsinks are a bit hot however.


If we replace the heat sink with something around 5 degrees/watt then the temperatures drop to

; with Heat sink of 5 degrees/watt
V(heatsink_temp3):        70
V(junction_temp3):        88
V(device_case_temp4):  73
V(ambient_temp3):         25
V(device_case_temp3):   73
V(junction_temp4):         88

Still a bit hot, try to get your heatsink down to 50, and keep an eye on junction temperature.

For an SSB transmitter that is transmitting uncompressed, unprocessed speech we can use 30% of peak power. Simply setting the current to 3 Amps (1.5A per device) gives us a thermal model for 10W PEP/3W average. The 3.5 degrees/watt heatsink is at 35 degrees and the junctions are at 40.

To really see the changes of temperature when pulses of power are applied, the dynamic changes  of temperature requires knowing the heat capacity, the thermal masses of the devices, this is rarely given in the datasheets but FesZ shows devices that summarize the data and this is enough to model and produce graphs of temperature against time.

Some transistors have thermal models that are either a Pi network of series resistors and capacitors to ground or a ladder network of parallel RC pairs in series. Inserting this in the thermal models and using a pulse or changing current source allows plotting the peak temperature against time. You could even apply a .WAV file of speech to the current source. See FesZ video for a good example.

Two problems remain, sometimes the data is missing or given in an alternative form. and secondly the environment around the heatsink matters a lot. A heatsink contained within a small case or fed an airflow from a fan changes the figures dramatically. This is why heatsinks are often mounted outside the case but you must allow clear space above and below the heatsink so that convection can take place and the heated air rises away from the heatsink and ambient temperature air is drawn in. 

You could use thermal data from a similar device in the same case/package. Better than nothing, but design conservatively. 

As regards fans, there are graphs for some heatsink datasheets that give improved thermal resistance values for various flow rates. Again, assuming the fan is fed cool ambient air and pushes the hot air away. 

The volume of a heatsink for a given flow condition can be obtained by using the following equation:

 Volume(heatsink) = volumetric resistance (Cm3 °C/W)/thermal resistance θSA (°C/W) 

An approximate range of volumetric resistance is given in the following table: (TI datasheet SLVA462)

Available Airflow Volumetric Resistance
 (LFM)                    (Cm3 °C/W)
 200                            150 - 250 
 500                              80 - 150
 1000                            50 - 80 
 NC                            500 – 800

The next important criterion for the performance of a heatsink is the width. It is linearly proportional to the performance of the heatsink in the direction perpendicular to the airflow. Considering an example, an increase in the width of a heatsink by a factor of two, three, or four increase the heat dissipation capability by a factor of two, three, or four.

Similarly, the square root of the fin length used is approximately proportional to the performance of the heatsink in the direction parallel to the airflow. In case of an increase in the length of the heatsink by a factor of two, three, or four only increases the heat dissipation capability by a factor of 1.4, 1.7, or 2. 

If the board has sufficient space, it is always beneficial to increase the width of a heatsink rather than the length of the heatsink.

The full calculation for the combined thermal resistance of a fan and heatsink is so complex you are better to use an online calculator or apply some simple rules of thumb.

A (over)simple rule of thumb is that a fan will decrease the thermal resistance of a heatsink by a factor of two to four. But there are a lot of "it depends" things to think about.

The maths behind some of it is described at https://www.heatsinkcalculator.com/blog/heat-sink-design-optimization-for-forced-convection/ if you enjoy working with hyperbolic Tan functions and Reynold numbers then knock yourself out...

The same website covers using PCB as a heatsink, which is often overlooked but is important particularly when using surface mount components. (https://www.heatsinkcalculator.com/blog/how-to-calculate-the-thermal-resistance-of-a-pcb/) these uses Bessel functions, wild maths...

A much more sophisticated approach is to use proper thermal modelling software that uses "fine white elephants" to quote an in-phrase we used to confuse students with. FEM, (Finite Element Method) analysis breaks down a 3D shape into small sections and uses boundary conditions to create matrices to be solved by numerical methods. Often used for calculating and visually displaying mechanical stress or electromagnetic field strengths, it can also solve dynamic (and static) thermal flow problems.

FreeCAD has an FEM workbench although I have not yet gone down that rabbit hole, curiouser and curiouser...





Wednesday, 3 April 2024

47 - MUF diving, a further look at getting from A to B

 MUF diving, a further look at getting from A to B

Last month we looked at the height and strength of the ionosphere. In that the Critical frequency FoF2 and the height either real or virtual (hmF2 or h’F2) are important for determining what frequencies get what distance. The MUF vs Range table at the bottom of the ionogram lists this.



For example Figure 1 lists MUF data for Dourbes in Belgium, again, I circle in red the important bits

The critical frequency of 5.7MHz means we will get a skip zone at 7MHz and we can talk to stations 3000km away on 14MHZ but 18MHz is a bit iffy since the maximum usable frequency is 18.17MHz.

That’s really a repeat of what we said last month, but it is important you understand it before looking at how things work planet wide.

This month I will show you world wide maps that are drawn by using the FoF2 and h’F2 downloaded from ionosondes all over the planet. You should take these with a pinch of salt because the global network of ionosondes is not always reliable, the server has been down 3 or 4 days this month already for instance. (by mid March)

I found a useful calculator at https://www.hfunderground.com/propagation/#skipzone It is about halfway down the page if the bookmark doesn’t get you straight to it. It should look like figure 2

As you see at the moment the calculation refers to, you cannot talk to anyone within 256 miles of you on 40m. If you live in the UK or near Europe.

We did not talk too much about how the ionosphere changes with time, both over a few tens of minutes as waves and “clouds” travel along the layers and over the course of day, or season, or year/decade. All have patterns that affect us.

One thing at a time. Over the day some of the ionosondes can give us a useful graph, https://www.propquest.co.uk/graphs.php can give us the graph below (I switched off the MUF and E layer data for this plot – click the symbols on the bottom line)

Figure 3 Plot of how FoF2 varies over the day

If the MUF factor is 3.19 (see the fat red oval in Figure 1) then DX is possible at 7.85 * 3.19 = 24.88MHz. even at 7pm you can use 15m and maybe, maybe! Even 12m…

Of course, we have only been using the ionosonde at Dourbes for the above, we should really use a station at the midpoint of our path. We need a map of where they are. Globally a lot of the data is brought onto a couple of servers. KC2G keeps a live list of ionosondes at https://prop.kc2g.com/stations/ and I just counted 48 “live” stations. He quotes the sources as the NOAA National Centers for Environmental Information (https://www.ngdc.noaa.gov/stp/IONO/rt-iono/ )and the Lowell Global Ionospheric Radio observatory. (https://giro.uml.edu/ ). Figure 4 below is from Lowell’s website.


But the NOAA site only takes data from the stations listed on Figure 5


The reason I am highlighting this as I am about to show you a worldwide MUF map. IF THE NUMBER OF STATIONS SUPPLYING DATA WAS HIGHER, THE MAP WOULD BE MORE ACCURATE. Again, you need a pinch of salt. Don’t believe everything the web (or anyone) tells you.

“Trust in God, for all others verify”

So, now a few screenshots from the web site of KC2G, please explore the site – it only has half a dozen pages, one is a dynamic list of ionosondes updated every 5 to 20 minutes, one is an explanation of the site and one is a MUF map, shown in figure 6.It does not reproduce very well as the colours are quite pale, for best results click https://prop.kc2g.com/ (the MUF tab)



So, geographically, at the time the map was plotted, we can work the Americas using highish frequencies but not East Europe or Asia.

But do take note that the number of stations that have contributed data to allow the graph to be plotted is quite small only 15 or so, and they are highly clustered (the yellow and green numbered circles in Figure 6).  The software is clever enough to interpolate as best it can but it cannot be accurate without sufficient accurate data. Be careful.

A further source of a map that attempts to show similar data comes from Australia, unfortunately they just plot critical frequency, but if you remember to multiply by the MUF ratio (roughly 3 ) you can see how well communication might work. The actual MUF ratio does depend on the height of the ionosphere and the intensity and thickness of all the layers but the map is still useful nonetheless. The Australian Space Weather Forecasting Centre has links for “GLOBAL HF” down the left hand side of its home page and the “IONOSPHERIC MAP” link produces figure 7 on the next page.

Be careful to click the 0° option under the “MAP CENTRE AT LONGITUDE” text.

At lease the map is highly coloured and easier to read then the faint colors(sic) of KC2G






And that is that, we have talked about the height and intensity of the F2 layer and how it creates a path for a single bounce radio contact. We can see how the conditions vary over the day.

We have not looked at multi-bounce communications, although it is just an extension of what we have covered here.

We have not looked at greyline communications and other weird communication paths due to strange going’s on in the ionosphere

We have not talked about the effect of magnetic fields, these are recorded as the A and K indices

We have not looked at solar storms and the several ways they can affect things.

All these just change the ionosphere, its intensity, height and profile of the various layers. I think understanding FoF2 and h’F2 is a good start!