Using Node-RED to read a HF propagation map

A few things to note.
1. This ‘trick’ should work for any sort of image that you need to compare color values.
2. I did not come up with the core code, I just asked the question.

HF propagation is tricky.
Thankfully a bunch of smart guys are running the show at the space weather services arm of the Australian BOM.
They produce cool maps that show you what frequency you need to use to be able to (hopefully) pick up distant stations.
The map is updated every hour for the past hour. While the data is an hour old, I have found that for what I need, one hour resolution is more than accurate enough.

Here is what a typical map looks like.

This one, taken at 3am in the morning (PST) is super ‘simple’, but it clearly shows how you need a higher frequency to cover more ground.
As simple as it is the burning question remains…. How to automatically select the frequency from the image and so command the receiver to switch to said frequency? And what is said frequency?

Enter Node-RED.

Using the image-tools node, you can read the color value for a given x,y value.
So, we set up a flow to first read and get the values of each part of the scale.
We then map those color values to the scale frequencies. So in effect you end up with a number that represents the value in MHz for any location on the map.
Here is a screen shot of the flow and I have clicked the 6MHz inject node to see the values in the debug tab.

So now that we have that raw color value for that part of the scale, any x,y location will yield its MHz value to us…..
Here is the flow to read the scale.
[{"id":"2cdd6244.f73a8e","type":"change","z":"fa1bd60e.5b6d3","name":"","rules":[{"t":"set","p":"pixel","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":1860,"wires":[["86c9c963.a16eb8"]]},{"id":"ceebb092.cb7d3","type":"inject","z":"fa1bd60e.5b6d3","name":"8mhz scale","topic":"","payload":"{\"x\":130,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1760,"wires":[["2cdd6244.f73a8e"]]},{"id":"10a35d70.4e06a3","type":"inject","z":"fa1bd60e.5b6d3","name":"2mhz scale","topic":"","payload":"{\"x\":55,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1640,"wires":[["2cdd6244.f73a8e"]]},{"id":"6b28b2b0.54f2ac","type":"inject","z":"fa1bd60e.5b6d3","name":"4mhz scale","topic":"","payload":"{\"x\":80,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1680,"wires":[["2cdd6244.f73a8e"]]},{"id":"3c247f0f.dcc16","type":"inject","z":"fa1bd60e.5b6d3","name":"6mhz scale","topic":"","payload":"{\"x\":105,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1720,"wires":[["2cdd6244.f73a8e"]]},{"id":"d182e0ef.f5ddf","type":"inject","z":"fa1bd60e.5b6d3","name":"10mhz scale","topic":"","payload":"{\"x\":155,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1800,"wires":[["2cdd6244.f73a8e"]]},{"id":"9926806f.ef6c9","type":"inject","z":"fa1bd60e.5b6d3","name":"12mhz scale","topic":"","payload":"{\"x\":180,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1840,"wires":[["2cdd6244.f73a8e"]]},{"id":"97a5a697.65b2b8","type":"inject","z":"fa1bd60e.5b6d3","name":"14mhz scale","topic":"","payload":"{\"x\":205,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1880,"wires":[["2cdd6244.f73a8e"]]},{"id":"5f692af2.01b914","type":"inject","z":"fa1bd60e.5b6d3","name":"16mhz scale","topic":"","payload":"{\"x\":230,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1920,"wires":[["2cdd6244.f73a8e"]]},{"id":"cb3382d2.55a9","type":"inject","z":"fa1bd60e.5b6d3","name":"18mhz scale","topic":"","payload":"{\"x\":260,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":1960,"wires":[["2cdd6244.f73a8e"]]},{"id":"191be3a3.5d41fc","type":"inject","z":"fa1bd60e.5b6d3","name":"20mhz scale","topic":"","payload":"{\"x\":280,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":2000,"wires":[["2cdd6244.f73a8e"]]},{"id":"fd826f9a.89454","type":"inject","z":"fa1bd60e.5b6d3","name":"22mhz scale","topic":"","payload":"{\"x\":305,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":2040,"wires":[["2cdd6244.f73a8e"]]},{"id":"c77c41f4.0a48d","type":"inject","z":"fa1bd60e.5b6d3","name":"24mhz scale","topic":"","payload":"{\"x\":330,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":2080,"wires":[["2cdd6244.f73a8e"]]},{"id":"d6762d1a.9717e","type":"inject","z":"fa1bd60e.5b6d3","name":"28mhz scale","topic":"","payload":"{\"x\":355,\"y\":70}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":2120,"wires":[["2cdd6244.f73a8e"]]},{"id":"86c9c963.a16eb8","type":"jimp-image","z":"fa1bd60e.5b6d3","name":"","data":"http://www.sws.bom.gov.au/Images/HF%20Systems/Global%20HF/HAP%20Charts/Phoenix.gif","dataType":"str","ret":"img","parameter1":"img.png","parameter1Type":"str","parameter2":"","parameter2Type":"msg","parameter3":"","parameter3Type":"msg","parameter4":"","parameter4Type":"msg","parameter5":"","parameter5Type":"msg","parameter6":"","parameter6Type":"msg","parameter7":"","parameter7Type":"msg","parameter8":"","parameter8Type":"msg","parameterCount":0,"jimpFunction":"none","selectedJimpFunction":{"name":"none","fn":"none","description":"Just loads the image.","parameters":[]},"x":610,"y":1860,"wires":[["c9ed63fd.f47b5"]]},{"id":"c9ed63fd.f47b5","type":"function","z":"fa1bd60e.5b6d3","name":"get value","func":"msg.image = msg.payload;\nvar x=msg.pixel.x;\nvar y=msg.pixel.y;\nmsg.payload = msg.image.getPixelColor(x, y); // returns the colour of that pixel e.g. 0xFFFFFFFF\nreturn msg;\n","outputs":1,"noerr":0,"x":760,"y":1860,"wires":[["42a68931.79f2e8","53feaf7a.64449"]]},{"id":"42a68931.79f2e8","type":"change","z":"fa1bd60e.5b6d3","name":"color to freq","rules":[{"t":"change","p":"payload","pt":"msg","from":"4278190335","fromt":"num","to":"2MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"4294902015","fromt":"num","to":"4MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"2155872511","fromt":"num","to":"6MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"8388863","fromt":"num","to":"8MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"16711935","fromt":"num","to":"10MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"16777215","fromt":"num","to":"12MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"8421631","fromt":"num","to":"14MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"65535","fromt":"num","to":"16MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"33023","fromt":"num","to":"18MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"2147483903","fromt":"num","to":"20MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"2147516671","fromt":"num","to":"22MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"4278255615","fromt":"num","to":"24MHz","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"2155905279","fromt":"num","to":"28MHz","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":910,"y":1860,"wires":[["a36c7916.597038"]]},{"id":"a36c7916.597038","type":"debug","z":"fa1bd60e.5b6d3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1080,"y":1860,"wires":[]},{"id":"53feaf7a.64449","type":"debug","z":"fa1bd60e.5b6d3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":920,"y":1940,"wires":[]}]

Next I simply looked up the lat/long for the other locations of interest to me (San Francisco and New York) and put those x,y values into another flow that reads them every hour.
The flow reads the color value of that pixel and does a look up for its value and converts that value to its MHz value.

Note how this flow looks identical to the one above so I am not going to include the code for it.

So now on my dashboard I can display the map and for any selected location on the map the predicted frequency needed to hear a station at that location for any given past hour.

I then use that frequency to map to the HF ACARS (HFDL) channel number and command the receiver in Phoenix to switch to it every hour.
Its not perfect, but its way way way better than manually keeping on top of all that.