Skip to content

Repository files navigation

ham.el

Amateur radio support for Emacs, by K6SM.

ham-rig.el is a transceiver control panel. It shows both VFOs' frequency and mode, passband, split, tuner, the receive front end and tuning step; an S-meter while receiving; and power, ALC, compression, SWR and supply voltage and current while transmitting. A second panel adjusts every level and switch the radio offers. It speaks to the radio through Hamlib's rigctld, and publishes what it sees on an event bus so other packages can follow the radio.

ham-spacewx.el is a space weather panel: solar flux, sunspot number, X-ray flux and its multi-day peak, proton flux, the planetary K and A indices, and the real time solar wind. It reports the NOAA R, S and G scales as they stand and as they peaked over the last day, and estimates the maximum usable frequency and band conditions for your own location.

ham-spot.el lists who is on the air, from a DX cluster and in time from the activation programs and reporting networks. Put the cursor on a spot, press RET, and the radio goes there — frequency and mode.

ham.el is the library underneath: an event bus, an asynchronous line transport, Maidenhead and great-circle geodesy, a band plan, and frequency parsing and formatting. It has no user interface.

Everything works in a terminal.

File Contents
ham.el Event bus, TCP transport, geodesy, band plan.
ham-rig.el Transceiver panel and controls panel.
ham-remote.el Audio transport for operating over a network.
ham-spacewx.el Space weather, NOAA scales and a propagation estimate.
ham-spot.el Spot list, and the key that tunes the radio to one.
ham-dxcluster.el DX cluster client.
ham-pota.el Parks on the Air activations.
ham-sota.el Summits on the Air activations.
ham-wwff.el World Wide Flora and Fauna activations.
ham-bota.el Worldwide Bunkers on the Air, over an event stream.

Requirements

  • Emacs 29.1 or later
  • Hamlib, for rigctld
  • curl, optional, for ham-spacewx — standard on macOS and Linux, and shipped with Windows since 10/1803. Without it the panel falls back to Emacs's own url.el, whose connection setup can block the editor when the network is unreachable.
  • An audio transport, optional, for ham-remote — Mumble is the one to start with, and Getting started with audio walks through it from nothing. trx and zita-njbridge are alternatives.

Developed on Emacs 29.3 and Hamlib 4.5.5, tested with a Yaesu FTDX10. For an FTDX10, use Hamlib 4.6 or later: 4.5.x cannot set its RF gain, and gets three other controls wrong (see When a control will not change). Checked against 4.7.2.

Installation

(add-to-list 'load-path "~/.emacs.d/lisp/ham/")
(require 'ham-rig)

Running rigctld

Start one rigctld for the radio and leave it running:

rigctld -m 1042 -r /dev/ttyUSB0 -s 38400

rigctl -l lists model numbers; 1042 is the FTDX10.

Run one rigctld and point every program at it — this package, WSJT-X, fldigi, your logger. It owns the serial port; opening that port directly locks out everything else on the station.

With no radio to hand, Hamlib's dummy works:

rigctld -m 1 -P RIG

-P RIG makes the dummy accept PTT, which it otherwise refuses.

ham-rig-host may be any machine, so a rigctld across the network works as well as a local one. wfview provides rigctld emulation, which gives you LAN remote operation with wfview carrying the audio.

The rig panel

M-x ham-rig opens the panel and connects.

  FTDX-10  localhost:4532  connected

  VFOA   USB      14.074.000   STEP 1 k
  VFOB   USB      7.074.000

  ATU OFF     BW 2400 Hz    SPLIT off
  IPO         ATT OFF       ROOF 3 kHz    AGC AUTO

  RX  ████▏░░░░░░░░░░░░░░░░  S3

Each frequency line opens with its VFO and mode. The large frequency is the VFO the receiver is on, and stays on top. The line under it is the other VFO: VFO B while you are on A, VFO A after v swaps them, and Sub or Main on a rig with two receivers. In split, the VFO that transmits is marked TX.

Below them are the tuner, filter width and split, and under those the receive front end the way the FTDX10's display shows it: preamp (IPO, AMP1, AMP2), attenuator, roofing filter and AGC. The two rows share columns. Each column is sized for the widest value it can hold, so the panel does not shift when a setting changes, or when the mode changes from USB to PKTUSB.

Key Action
↑ ↓ Tune one step
M-↑ M-↓ Tune ten steps (also PgUp PgDn)
← → Smaller / larger step
. Choose a step
f Set frequency in kHz: 14074, or 14.074 MHz, or 14.074.000
m Set mode; with C-u, its filter width too
w Set the filter width
b Change band
v Swap VFO
s Toggle split
t Key or unkey
T Panic unkey
u Antenna tuner in or out
A Run the tuner's tuning cycle
P Switch the radio on or off
C Controls panel
g Poll everything now
c d Connect / disconnect
? h Every key, in a buffer
i Capabilities the radio reported
S Link statistics

The other VFO

It is read once a slow poll with \get_vfo_info, which Hamlib added in 4.1. A rig that cannot read a VFO without switching to it would flip to the other VFO and back every second, so on such a rig the line is left off. Hamlib 4.6 and later say so in Targetable features; 4.5 in Has targetable VFO. i shows both.

ham-rig-show-other-vfo overrides that: t shows it on any rig, nil on none. ham-rig-other-vfo returns it as ("VFOB" 7074000 "USB").

Front-end indicators

ham-rig-panel-indicators chooses them, in order, as (HAMLIB-NAME . LABEL). The default is preamp, attenuator, roofing filter and AGC. Only the ones the rig has appear, and any level or switch from the controls panel can be added:

(add-to-list 'ham-rig-panel-indicators '("NB" . "NB") t)

An entry with no label (the preamp, by default) shows a named value alone, so a Yaesu shows AMP1 rather than PREAMP AMP1. Those names come from ham-rig-manufacturer-value-labels, which covers Yaesu's preamp, and from ham-rig-control-value-labels, which overrides it. A rig without names for its values shows PREAMP 10 dB.

The indicators are read two per slow poll (ham-rig-indicator-poll-batch), and all at once on connecting and on g. A change made from the controls panel shows immediately; one made at the radio shows within a few seconds.

The FTDX10's roofing filters are corrected automatically. Hamlib's FTDX10 table was adapted from the FTDX101's, whose filters are 12 kHz, 3 kHz, 1.2 kHz, 600 Hz and 300 Hz. The 1.2 kHz entry was removed and 600 Hz became 500 Hz, but the codes kept their places, so the FTDX10's codes skip 3. The names Hamlib publishes for them were written as a list with no gap, and from 500 Hz on every name is one off:

Code Hamlib sends Filter it selects Hamlib's name for the code
1 12 kHz 12 kHz
2 3 kHz 3 kHz
3 (none: falls through to the last entry, 300 Hz) 500 Hz
4 500 Hz 300 Hz (optional)
5 300 Hz (no name)

The radio also never reports Hamlib's first entry, AUTO. So for an FTDX-10, ham-rig replaces Hamlib's list with the radio's four filters under the codes Hamlib actually sends and reads: 12 kHz, 3 kHz, 500 Hz and 300 Hz. The panel and the controls panel both use it, and choosing a filter selects that filter. This is in ham-rig-model-control-values, which can correct any other model the same way. It is present in every Hamlib version up to and including 4.7.2 and its development branch; checked against the source, and by driving 4.5.5 and 4.7.2 against a simulated FTDX10.

Tuning

↑ and ↓ tune by the current step; ← and → change the step, which is shown beside the frequency. Steps run from 1 Hz to 1 MHz, set by ham-rig-tuning-steps and ham-rig-default-tuning-step.

The readout moves on the keypress and the next poll corrects it to what the radio settled on, so holding a key tunes smoothly. Only the newest frequency is sent.

Transmitting

While keyed, the S-meter is replaced by a bar for each meter the radio reports, and the elapsed transmission time:

  TX   12s
  PWR  ██████████████████░░░░  85 W
  ALC  ███████░░░░░░░░░░░░░░░
  COMP ██████████░░░░░░░░░░░░  12 dB
  SWR  ████████░░░░░░░░░░░░░░  2.40
  VDD  ███████████████████░░░  13.2 V
  ID   █░░░░░░░░░░░░░░░░░░░░░  1.8 A
  • SWR runs 1:1 at the left to infinity at the right, scaled by reflection coefficient. Amber past 2:1, red past 3:1.
  • ALC is a bar alone — the number Hamlib reports for it has no units. Amber past half scale, red past 80%.
  • Power reads in watts, converted by the radio.
  • Compression, supply voltage and current read in dB, volts and amps, as the radio reports them.

ham-rig-tx-meters chooses which meters appear, and only those the radio reports are shown.

Two settings control how a meter is drawn:

  • ham-rig-meter-zones sets where a bar turns amber and red. The ALC thresholds are a starting point, not a specification — Hamlib does not report where a radio's own ALC zone ends. Compare the bar against the radio's ALC marking and move them to match.
  • ham-rig-meter-ranges sets the range a bar is drawn over, where the range Hamlib declares does not describe the readings. An FTDX10 declares supply voltage as 0 to 1 and then answers 13.25, so a bar drawn over the declared range sits at full scale whatever the radio is doing. A bar stuck at one end means the range here needs correcting for your radio.

Transmit safety

  • A watchdog unkeys the radio if a transmission runs too long.
  • Unkeying is confirmed, not assumed. After commanding the radio off, ham-rig reads PTT back. If the radio still reports itself transmitting it commands the unkey again, up to ham-rig-unkey-attempts times, and warns you to use the front panel if it never succeeds.
  • Emacs unkeys on exit.
  • Losing the link while keyed warns, and unkeys again on reconnection.
  • The event bus never keys the radio. Only a direct keystroke transmits.

The watchdog budget is ham-rig-tx-timeout (180 s) when the length of a transmission is not known in advance, as with live voice. Where the length is known, a caller declares it with ham-rig-expect-transmission and the budget becomes that duration times ham-rig-tx-watchdog-margin, bounded below by ham-rig-tx-watchdog-floor and above by ham-rig-tx-timeout-max. The antenna tuner cycle uses this: it is held to about 25 seconds rather than three minutes, so a tuner that jams keys the transmitter for seconds instead of minutes.

CAT has no dead-man behaviour: if the control link dies mid-transmission, nothing in software can unkey the radio. Enable your transceiver's own TX timeout timer.

The controls panel

C, or M-x ham-rig-controls.

  FTDX-10   controls

  LEVELS
    PREAMP             ######.... AMP1    IPO/AMP1/AMP2
    RFPOWER            #####..... 50 W    5%..100%
    DNR                ####...... 40%     0%..100%
    MICGAIN            ####...... 35%     0%..100%
    KEYSPD             ###....... 22      4..60
    Filter width       #####..... 2.4 kHz 200 Hz..4 kHz
    IF                 #####..... -200    -1200..1200

  FUNCTIONS
    TUNER              on
    VOX                off

  THIS RADIO
    Roofing filter     #####..... 3 kHz   AUTO/12 kHz/3 kHz/500 Hz/300 Hz (optional)
    Contour            .......... off     off/on
Key Action
← → Adjust, or toggle a switch
M-← M-→ Adjust ten steps (also - +)
RET SPC Toggle a switch, or set a level
= Type a value
g Re-read everything
? h Every key, in a buffer

The list is built from the radio's own \dump_caps report, so it shows what this radio has: IF shift, notch, transmit power, noise reduction, noise blanker, CW speed and pitch, mic gain, VOX gain and delay, monitor level, compression, break-in delay, preamp, attenuator, squelch, AF and RF gain, and switches for the tuner, VOX, ANF, APF, manual notch and RIT. A different radio gives a different panel.

Three sections: Hamlib's standard levels, its standard switches, and under THIS RADIO the backend's own — see Roofing filter, contour, and the rest of this radio.

Read-only meters stay out of this panel. Controls the radio reports with no usable range are omitted, unless the rig describes them some other way.

AGC is the case that matters. Hamlib reports it as a level with the range 0..0/0, which looks like nothing to adjust, but it lists the positions separately under AGC levels — 0=OFF 1=SUPERFAST 2=FAST 5=MEDIUM 3=SLOW 6=AUTO 4=USER on a rig with seven. The panel reads that list, so AGC appears with the rig's own names for its own positions, and a radio with four gets four.

A switch is held to its positions, not to its range. For the preamp, the attenuator and AGC those are different things, and the range is the misleading one. An FTDX10 declares PREAMP(10..20/10) while its preamp has three positions — off, 10 dB and 20 dB — so the declared range does not contain off at all; it declares ATT(12..12/0) while its attenuator has four positions; and it declares AGC(0..0/0). The positions come from the Preamp:, Attenuator: and AGC levels: lines instead, and a value sent to one of these controls is snapped to the nearest position on that list.

Units

Hamlib reports most levels as a fraction rather than in the radio's own units.

  • Transmit power reads in watts, converted by the radio.
  • Preamp and attenuator read as switch positions.
  • Everything else normalised reads as a percentage.

Two limits are worth knowing:

  • Hamlib names a preamp position 10dB, not AMP1. Yaesu's words are built in (ham-rig-manufacturer-value-labels); supply your radio's own with ham-rig-control-value-labels and ham-rig-control-labels:

    (setq ham-rig-control-value-labels
          '(("PREAMP" (0 . "IPO") (10 . "AMP1") (20 . "AMP2")))
          ham-rig-control-labels '(("NR" . "DNR")))
  • Where Hamlib has already flattened a scale, the original is unrecoverable. The FTDX10's DNR runs 1 to 15 on the radio; Hamlib presents 0 to 1 in tenths, so it reads as a percentage.

A percentage moves by one percent a press. Hamlib reports the step of several of these levels as one 255th, because that is what fits in the byte the radio is sent — and four tenths of a percent rounds to the number already on the screen, so the key looks broken while working perfectly. ham-rig-percent-step sets the floor.

When a control will not change

rigctld answers every change with a return code, and anything but RPRT 0 means the radio was not changed. The panel reports it in the echo area and on the control's own line:

    RF                 ########## 100%    0%..100%  not set: timed out waiting for the rig (RPRT -5)

The note stays until a change to that control goes through. A refusal can take several seconds to arrive, because Hamlib retries the command before giving up. ham-rig waits for that answer even after it has stopped waiting for the request, then reads the control again.

The panel sends the levels Hamlib documents (L RF 0.5 for half RF gain, L PREAMP 10, L AGC 2), so a refusal is usually the rig or the Hamlib backend. rigctl -m <model> -r <port> -vvvvv L RF 0.5 prints the bytes Hamlib sends, which settles which one it is.

Hamlib 4.5.x with an FTDX10. Checked against 4.5.5 and 4.7.2, driven by the same simulated FTDX10:

Control 4.5.x 4.6 and later
RF gain Never set. The command goes out as RG, cut short by a NUL byte where the VFO digit belongs, and the radio refuses it (RPRT -5). RG0nnn;
Monitor level Set out of 100 but read back out of 255: set 50%, read 20%. Each press moves it further down. Correct.
NB level 1 to 10 are all sent as 10, and read back as 1. Correct.
VOX delay Set as the manual's two-digit code, read back as a tenth of the value. Correct.

Upgrading Hamlib fixes all four. The Windows installer is at hamlib.github.io.

The speech processor switch on an FTDX10 is handled by ham-rig. For the COMP switch (not the COMP level beside it), every Hamlib version up to and including 4.7.2 and its development branch sends an FTDX10 PR1; to switch it and PR; to read it. The radio answers ?; to both, which shows as -- and as not set: … (RPRT -14). Yaesu's CAT reference gives PR, then 0 for the speech processor (1 is the parametric microphone equaliser), then 1 for OFF or 2 for ON: PR02; switches it on, PR01; off, and PR0; reads it. Hamlib's PR1; is the equaliser with no state, and PR; a read with nothing to read.

So for an FTDX-10, ham-rig switches and reads it itself with the manual's commands, passing them to the radio through rigctld's W (raw command). A change is read back, so if the radio does not take it, the line says so. This is in ham-rig-model-raw-functions, which can correct a switch on another model the same way.

M-x ham-rig-send-raw sends any CAT command the same way and shows the radio's answer, for checking what a radio makes of a command without Hamlib in between: PR0; answers PR01; (off) or PR02; (on), and PR1; answers ?;.

Checked against Yaesu's CAT reference

Everything the panels send an FTDX10, through Hamlib or directly, has been compared with Yaesu's FTDX10 CAT Operation Reference Manual (2308-F):

Control Command Agrees with the manual
Frequency, mode, VFO, split FA FB MD0 VS ST FT Yes
RF gain, AF gain RG0 / AG0 000–255 Yes, from Hamlib 4.6 (4.5.x sends RG cut short)
Preamp PA0 0 IPO, 1 AMP1, 2 AMP2 Yes, and ham-rig uses Yaesu's names
Attenuator RA0 0 OFF, 1–3 6/12/18 dB Yes
AGC set GT0 0–4; answered 0–6, 4–6 being AUTO-FAST/MID/SLOW Yes; Hamlib reads all three as AUTO
Roofing filter set RF0 1/2/4/5; answered 6/7/9/A; no 3 Yes, with ham-rig's correction of Hamlib's names
Tuner AC00 0 off, 1 on, 2 tune Yes
Speech processor PR0 1 OFF, 2 ON Yes, sent by ham-rig; Hamlib's PR1; does not
Processor level, mic gain, power PL / MG 000–100, PC 005–100 Yes
Monitor level ML1 000–100 Yes, from Hamlib 4.6
NB level, DNR level NL0 000–010, RL0 01–15 Yes, from Hamlib 4.6
Break-in delay SD 00–33 Yes
IF shift, notch, contour/APF IS0, BP0, CO0 Yes
Power on dummy PS1;, then PS1; within 1–2 seconds Yes (USB only; not over RS-232C)

One is ambiguous in the manual itself: VOX delay (VD) lists two-digit codes (00 = 30 ms … 33 = 3000 ms) but draws four digit positions. Hamlib sends the two-digit code. M-x ham-rig-send-raw VD; shows which the radio uses.

Filter width

The receive filter width is not a Hamlib level. It travels with the mode — M <mode> <width> sets both, m reports both — so w sends the mode the rig last reported along with the new width, and C-u m sets the two together.

It is also in the controls panel, immediately before IF shift, which is where it sits on the radio and next to the other controls that shape the passband. The panel builds itself from Hamlib's level list, and the width is not on it, so that row is made rather than discovered: it reads through m and writes through M. Otherwise it behaves like RFPOWER or IF shift — ← and → slide it in steps, and the row shows the range it may be set to.

That range is continuous rather than a short list of presets. Most backends declare RIG_FLT_ANY, meaning the radio takes whatever width it is sent: the FTDX10 does, and it adjusts in 50 Hz steps across the whole SSB range, which a handful of named filters does not reach. ham-rig-passband-ranges holds the range and step per mode, widened by any filter list the backend does publish under Filters, so a rig offering something outside the default still gets it. Change mode and the range changes with it.

Roofing filter, contour, and the rest of this radio

Hamlib has two kinds of level. The ones under Set level are its standard set — named by the library, the same on every radio. The ones under Extra levels are the backend's own, and that is where anything one manufacturer does on its own lives. On a Yaesu that is the roofing filter, the contour, the audio peak filter and the keyer.

The controls panel reads both. Extension levels get their own heading, since they are the controls this radio has and a generic one does not:

  THIS RADIO
    Roofing filter     #####..... 3 kHz   AUTO/12 kHz/3 kHz/500 Hz/300 Hz (optional)
    Contour            .......... off     off/on
    Contour frequency  #####..... 1600    10..3200

The names and the positions come from the rig. Hamlib prints each one as a block naming its type, its label and either its range or its values, and the panel reads all of it — so the roofing filter offers exactly the filters fitted, under the words Yaesu uses for them.

These are read and written with the same l/L commands as any other level: rigctl looks a name up in the standard set first and falls back to the backend's own, so nothing special is needed to reach them.

Operating remotely

ham-rig-host already reaches a rigctld on any machine. ham-remote.el adds the audio, in both directions, and M-x ham-remote shows what it is doing:

  trx   radio.local   compressed

  RX audio  running   142s
  TX audio  running   142s

  PTT  receiving
Key Action
s S Start / stop the audio
r Restart
R Show what to run at the radio end
M Mumble client settings
w Write a Mumble server configuration
g Refresh

No audio passes through Emacs. External programs carry it, and this package starts them, restarts them if they die, sequences them against PTT, and reports their state.

Getting started with audio

If you have not used Mumble before, start here. Mumble is a voice chat system; we are using it as a two-way audio link with a radio on one end. Nothing about it is ham-specific, which is why its defaults are wrong for us in a few important ways — this section gets you from nothing to a working link.

Three pieces, on two machines.

   your desk                          the radio
  ┌──────────────┐                  ┌──────────────────────┐
  │ Mumble       │                  │ Mumble client        │
  │ client       │ ◄──────────────► │ (mic = receiver out, │
  │ (headset)    │      audio       │  speaker = rig mic)  │
  │              │                  │                      │
  │ Emacs        │ ◄──────────────► │ rigctld              │
  │ ham-rig      │   rig control    │                      │
  │ ham-remote   │                  │ Mumble server ◄──────┼── usually here
  └──────────────┘                  └──────────────────────┘

Both ends run a Mumble client. One machine also runs the Mumble server, which the two clients meet on. The server normally lives at the radio end — a Raspberry Pi is plenty — but it can be anywhere both ends can reach.

The server is what makes this easier than a direct connection: only one machine needs a reachable address, and it is the one that stays put.

1. Install

Client Server
Debian, Ubuntu, Raspberry Pi OS sudo apt install mumble sudo apt install mumble-server
Fedora sudo dnf install mumble sudo dnf install mumble-server
Arch sudo pacman -S mumble sudo pacman -S murmur
macOS brew install --cask mumble run it on Linux instead
Windows installer from mumble.info installer from mumble.info
FreeBSD pkg install mumble pkg install murmur

Install the client on both machines and the server on one of them.

The server binary is called mumble-server on newer packages and murmurd on older ones; ham-remote looks for both, so you do not need to know which you have.

2. Set up the server

On Debian and Raspberry Pi OS the package asks the important questions for you:

sudo dpkg-reconfigure mumble-server

Say yes to starting at boot. It will ask you to set a SuperUser password — that is the server's administrator account, and you only need it if you later want to change server settings from inside a client. Write it down anyway.

Elsewhere, or to set it again later:

sudo mumble-server -ini /etc/mumble-server.ini -supw YOUR-PASSWORD

On older packages that binary is murmurd; the arguments are the same.

Then replace the stock configuration with one tuned for a radio link. In Emacs, on the machine that will run the server:

M-x ham-remote-mumble-write-server-config

That writes a file (see ham-remote-mumble-server-config for where). Copy it over /etc/mumble-server.ini, keeping a backup, and restart the service:

sudo systemctl restart mumble-server

Run it under the system's own service manager rather than from Emacs: the server should be up whether or not anyone is logged in. ham-remote can run it — set ham-remote-mumble-run to server or both — which is handy for trying the whole thing out on one machine before you commit to wiring.

What it changes and why is in Mumble below; the short version is that it forces Opus, keeps the user count small, and stops the server writing a log that would wear out an SD card.

Open the port. Mumble uses 64738, both TCP and UDP. TCP carries the control connection and UDP carries the voice; if UDP cannot get through, Mumble still works but routes voice over TCP, which is noticeably worse. If the radio is across the internet rather than the house, forward both on the router, or — better — put both machines on a VPN and skip the forwarding entirely. You want the VPN anyway: rigctld has no authentication of its own.

3. Set up the client at your desk

Run mumble once by hand before involving Emacs. On first launch it offers two wizards:

  • The Audio Wizard picks your input and output devices and sets levels. Run it — device selection is the fiddly part and it does it well.
  • The Certificate Wizard creates your identity. Mumble authenticates by certificate rather than by password, so accept the default and let it make one.

Then undo some of what the Audio Wizard did. It is tuned for a headset in a quiet room and will have enabled things that ruin a radio link. Press M in the ham-remote panel — or M-x ham-remote-show-mumble-setup — for the full list; the ones that matter are echo cancellation, noise suppression and gain control (all off) and transmit mode (Push To Talk).

Now tell ham-remote where the server is:

(setq ham-remote-host "radio.local"        ; the machine running the server
      ham-remote-transport "mumble"
      ham-remote-mumble-user "K6SM"        ; your callsign
      ham-remote-mumble-run 'client)

M-x ham-remote, then s. The panel should show Mumble running.

4. Set up the client at the radio

The radio end runs a Mumble client too, with two differences: its microphone is the receiver rather than a person, and it transmits continuously — there is nobody there to key it.

Wire the audio first. You need the receiver's audio going into the machine's input, and the machine's output going into the transmitter's audio input. Most modern transceivers present a USB sound device that does both; otherwise an interface like a SignaLink sits between. Pick that device — not the machine's built-in one — in Mumble's Audio Wizard.

R in the panel prints the exact command and settings for this end.

Set the transmit level with the rig's ALC meter, not by ear: bring the audio up until ALC just begins to move and stop there.

5. Check it works

  1. At your desk, M-x ham-remote and s. The panel shows Mumble running.
  2. You should hear the band. If not, the problem is at the radio end's input.
  3. M-x ham-rig, connect, and key with t. The panel's MIC should flip from shut to open, and the radio should transmit your voice.
  4. Unkey. MIC goes back to shut.

That MIC line is worth watching. ham-remote holds the Mumble microphone closed except while the rig is keyed, so your shack is not on the air between overs — but that only works if Mumble is in Push To Talk mode. If MIC says open when you are not transmitting, that setting is wrong.

When it does not work

Symptom Usually
No audio either way Server not reachable: check port 64738 TCP and UDP
Audio breaks up Raise the jitter buffer 10 ms at a time
Everything sounds far away and thin Noise suppression or AGC still on
Weak signals vanish into silence Noise suppression
Digital modes will not decode Any of the three processors; or use a lossless back end
First syllable clipped Mumble in voice-activated mode, not Push To Talk
MIC open with the rig unkeyed Mumble not in Push To Talk mode
Delay grows the longer you talk Buffering somewhere; restart the client
Hum on transmit Ground loop — an isolating interface, not a software fix

M-x ham-remote-show-mumble-setup lists every setting and what it is for.

Choosing a transport

Which program carries the audio is a back end, selected by name in ham-remote-transport:

Back end Carries Needs
trx Opus over RTP; low bandwidth trx
zita-njbridge Uncompressed samples zita-njbridge, JACK
mumble Opus through a Mumble server mumble, mumble-server

Every command is a list of strings you can edit — ham-remote-trx-transmit-command and friends — with %h for host, %p port, %d device, %r sample rate, %c channels, %m the program, %u user, %n channel and %U a mumble:// URL. Correct them there if your build's options differ; no code changes are needed. Register your own back end with ham-remote-register-transport.

trx and zita-njbridge are pairs of one-way pipes: one process per direction. Mumble is not — one client carries both ways, and a server sits in the middle. A back end says which processes it needs, so both shapes work.

For trx, the latency control is the Opus frame size, -f, in samples: at 48 kHz the codec permits 120, 240, 480 or 960 — 2.5, 5, 10 or 20 ms. The receiver's jitter buffer, -j, trades delay against tolerance of an uneven network.

Mumble

Mumble is the one back end that is not a pair of pipes between two hosts. A server sits in the middle, which is why it is worth having: it crosses NAT from both sides, it survives an address that changes, and more than one person can listen to the same radio.

ham-remote-mumble-run says which halves this machine runs — client at the operator end, server on the machine at the radio end, both to try it on one box. M-x ham-remote starts and stops whichever apply, watches them, and restarts them if they die.

The microphone follows the transmitter. Mumble is otherwise open all the time, which puts the shack on the air between overs, or listening for a voice, which clips the first syllable and opens on a cough. With ham-remote-mumble-follow-ptt set, keying the rig runs mumble rpc starttalking and unkeying runs stoptalking, so the microphone is open only while the transmitter is. The panel shows MIC open or MIC shut — an open microphone nobody noticed is the failure worth seeing. This requires Mumble to be in Push To Talk mode; in continuous or voice-activated mode the gating does nothing.

M prints the client settings a remote station wants. The ones that matter:

Setting Value Why
Echo cancellation off
Noise suppression off A weak signal is exactly what it removes
Amplification / AGC off
Transmit Push To Talk So the rig's PTT can drive it
Audio per packet 10 ms The main latency control
Quality 72 kb/s ≥64 kb/s enables Opus low delay mode
Jitter buffer 20 ms, then raise Jitter breaks audio; latency alone does not
Text to speech, sounds off They would go out over the air

The first three are on by default and are each a model of a human voice in a quiet room. What crosses this link is often neither — a signal at the noise floor, or a modulated waveform carrying data. Leave them on and the band sounds dead and digital modes stop decoding.

w writes a server configuration. It sets opusthreshold=0, which forces Opus whatever connects: a server falls back to CELT the moment one old client appears, which costs more CPU and sounds worse. On a Raspberry Pi Zero 2W that is the difference between working and not. It also keeps users small and sets logdays=0, since the server writes its log to SQLite and on a machine booting off an SD card those writes are what wears it out.

The server neither mixes nor transcodes — it forwards packets — so its load is per-client crypto and networking rather than audio work. A Pi handles a remote station's two or three clients comfortably.

Mumble authenticates by certificate, so no password appears on any command line, where every process list on the machine could read it. A server that needs one should be saved in the client's own server list.

Mumble compresses, so ham-remote-require-lossless refuses it — see below.

Compressed audio and digital voice

A back end declares whether it preserves the waveform. This matters for digital voice: what crosses the link is then a modulated waveform rather than speech, and a codec built to model the human voice will destroy it. Set ham-remote-require-lossless and a compressing back end is refused, naming one that would do instead.

The radio end

The radio end runs its half under systemd, so it starts at boot and does not depend on Emacs. R in the panel, or M-x ham-remote-show-radio-end, prints the two commands for your current configuration, along with the rigctld invocation.

Both need a tunnel: rigctld has no authentication or encryption at all. WireGuard suits this better than SSH, whose forwarding is built for TCP while the audio is UDP.

Not yet done

Nothing at the operator's end can unkey the transmitter once the network is gone. This is the same gap CAT has, made likelier by a longer link. A radio-end watchdog is the next piece of work. Until it exists, remote operation rests on the transceiver's own transmit timeout — enable it.

Spots

M-x ham-spots opens the list of stations that have been heard. Put the cursor on one and press RET: the radio tunes to it, in the right mode.

  Spots   8 shown
  RET tune  f filter  m mode  b band  s sort  F clear filters  ? keys

  cluster: dxc.nc7j.com, DXSpider  31 spots   POTA: 24 spots, 40s ago

    18100.0 17m  FT8   JR1FYS      1m  dxcluster LOUD in FL
    14320.0 20m  SSB   K4ABC       2m  pota      K-1234 Shenandoah NP
     7005.0 40m  CW    VP8ABC      4m  dxcluster up 2
    14062.0 20m  CW    G0ABC       6m  sota      G/LD-007 Great Gable
    14025.0 20m  CW    K1ABC       9m  dxcluster 599 NH
     7180.0 40m  SSB   W1XYZ       11m pota      K-0055 Acadia NP

One list, in age order, whatever feed each spot came from — the networks are interleaved rather than stacked in blocks.

Key Action
RET, . Tune the radio to this spot, frequency and mode
SPC Tune to it, leaving the mode alone
n, p, ↑, ↓ Move
d Everything known about this spot
f Show only spots matching a regexp
x Hide spots matching a regexp
m Show only certain modes
b Show only certain bands
a Show only the last so many minutes
F Clear every filter in this panel
s Sort by age, frequency, callsign or source
g Ask every source for an update
c Discard spots — this panel's feed only, if it shows one
1 One list for every feed
2 A window for each feed
o Only this spot's feed
? This list

Filtering

Every filter belongs to the panel it was set in. Two windows side by side are two independent things: narrowing the parks to 20 metres leaves the cluster beside it showing everything.

m takes a family or an exact mode:

Asked for Finds
SSB spots marked SSB, USB or LSB
PHONE those, plus AM and FM
CW CW, CWR
DATA FT8, FT4, RTTY, JS8, PSK31, WSPR and the rest
FT8 only FT8

so m then SSB,CW is phone and CW and nothing digital. The families are ham-spot-mode-groups and can be edited. A spot whose mode nobody reported is always shown — it is still a station on a frequency, and hiding it would lose real spots, which is the opposite of what asking for a mode is for.

f and x are the two halves of a text filter, matched against the callsign, mode, band, spotter, reference, park or summit name and comment together. x usually gets more use: naming the one thing to hide is easier than naming everything else.

  DX cluster   1 shown of 31
  RET tune  f filter  m mode  b band  s sort  F clear filters  ? keys

  cluster: dxc.nc7j.com, DXSpider  31 spots
  showing CW SSB   last 5 min

     7005.0 40m  CW    VP8ABC      4m  up 2

A panel that is hiding anything says so under the header. A filter set an hour ago and forgotten otherwise looks exactly like a quiet band.

ham-spot-max-age (60 minutes) is how long a spot is held, for every panel. a sets how much of that one panel shows, so one window can watch the last ten minutes while another keeps the hour.

One list, or one window each

M-x ham-spots shows every feed in one list, with a column saying where each spot came from. M-x ham-spots-separate gives each feed a window of its own, and ham-spot-separate-buffers makes that the default.

  DX cluster   31 shown          │  POTA   24 shown
  RET tune   f filter   b band … │  RET tune   f filter   b band …
                                 │
  cluster: dxc.nc7j.com          │  POTA: 24 spots, 40s ago
                                 │
    14074.0 20m  FT8  JR1FYS  12s│    14320.0 20m SSB K4ABC 30s K-1234 Shenandoah
     7005.0 40m  CW   VP8ABC  1m │     7180.0 40m SSB W1XYZ 2m  K-0055 Acadia NP

Each panel is wholly independent: its own filters, its own sort order, its own age window and its own cursor position, kept when the window is closed and reopened. c clears only that feed. A panel showing one feed spends the source column on the park or summit name instead, since every row in it would say the same thing.

Worth having when the feeds are being used for different things: a contest weekend's cluster produces spots faster than anyone can read, and in a combined list it buries the handful of park and summit activations that were the reason for looking. M-x ham-dxcluster, M-x ham-pota and M-x ham-sota each open just that one.

The ham-spot-filter, ham-spot-modes, ham-spot-bands and ham-spot-sort settings are what a new panel starts from, not state the panels go on sharing.

The DX cluster

(require 'ham-dxcluster)
(setq ham-dxcluster-call "K6SM")        ; required; there is no default
(setq ham-dxcluster-host "dxc.nc7j.com")
(setq ham-dxcluster-port 7373)

M-x ham-dxcluster opens the panel and connects. A cluster logs who is on it, so it will not connect until ham-dxcluster-call is set.

On Windows this needs nothing installed. Clusters are always described as telnet hosts, and Windows has not shipped an enabled telnet client since XP — but telnet to a cluster is a bare TCP socket carrying lines of text, and Emacs has opened those on every platform for decades. Nothing here shells out.

A cluster with no filter sends every spot on earth, which on a contest weekend is unreadable. Filters are the cluster's own, and go in ham-dxcluster-commands:

(setq ham-dxcluster-commands
      '("set/filter band/pass 20,40,17"      ; DXSpider
        "set/filter dxcc/reject k"))

The three families of cluster software take different commands, so the banner is read on connecting to find out which one answered — a Spider wants set/qra, an AR-Cluster wants set station grid, and sending the wrong one earns a page of help text or, on one of them, a disconnection. M-x ham-dxcluster-show-log shows the whole conversation, which is where to look when the panel stays empty.

M-x ham-dxcluster-spot posts a spot of your own to the network. It confirms first: that one is not undoable and not anonymous.

Parks, summits, nature reserves and bunkers

(require 'ham-pota)     ; Parks on the Air
(require 'ham-sota)     ; Summits on the Air
(require 'ham-wwff)     ; World Wide Flora and Fauna
(require 'ham-bota)     ; Worldwide Bunkers on the Air

Nothing to configure and no account needed — all four publish their current activations openly.

(setq ham-pota-programs '("K" "VE"))     ; North American parks only
(setq ham-sota-associations '("W7W"))    ; one SOTA association
(setq ham-wwff-programs '("KFF"))        ; one WWFF national programme
(setq ham-bota-programs '("G" "GM"))     ; British bunkers
(setq ham-pota-include-rbn nil)          ; people, not skimmers

Canadian Parks on the Air is POTA's VE references rather than a separate network, so ham-pota-programs covers it. Plenty of references are both a park and a nature reserve, so the same operator turns up in the POTA and WWFF panels under two different numbers.

No two of these agree on how to send a frequency. All four send a bare number with nothing saying what unit it is:

Feed Frequency Timestamp
POTA kHz, as a string UTC text, no zone marker
SOTA MHz, as a string UTC text, no zone marker
WWFF kHz, as a number Unix epoch seconds
WWBOTA either, as a number ISO text

A parser written for one and pointed at another is wrong by a factor of a thousand, which puts the radio in a different band without anything looking amiss. WWBOTA is the awkward one: it sends 14.285 sometimes and 14285 others, so the magnitude decides — in the amateur bands no number is ambiguous, since 14 kHz and 14 MHz are not both places somebody is activating from.

The timestamps are the other trap. Read UTC text as local time and every spot arrives hours old, ages straight out of the panel, and the panel shows nothing with no indication why.

The old SOTA host still answers. api2.sota.org.uk is retired but returns HTTP 200 with placeholder records saying DEPRECATED instead of failing, so a client pointed at it looks like it is working and shows fiction. ham-sota-host defaults to api-db2.sota.org.uk, and the answer is checked for those placeholders so a stale setting produces a plain error.

POTA, SOTA and WWFF are polled every two minutes, never more often than once a minute — SOTA's and WWFF's published rule, and ordinary politeness towards POTA. Each answer is the whole current list, so it replaces what that feed had rather than merging into it: an activator who has packed up is simply absent next time, and merging would leave them on the panel for an hour looking exactly like somebody still calling CQ.

Bunkers, and the event stream

WWBOTA is not polled. It holds one HTTPS request open and pushes each spot down it as Server-Sent Events, so ham-bota opens a single connection and leaves it there. ham-bota-backlog-hours says how much history to send first, which is why the panel has something in it immediately rather than waiting for whoever is next on the air.

That makes it the only feed here that is neither a poll nor a telnet session, and the reason the shared transport grew TLS and a raw-text mode. An HTTP body arrives in chunks, each introduced by its length in the same stream as the content, and a reader splitting the lot on newlines both hands those lengths to the caller as content and loses any event large enough to be split across two chunks. ham-bota undoes the chunking before it looks for lines.

WWBOTA also says when somebody has stopped: a spot marked QRT removes that activator from the panel rather than adding anything. No other feed here does that, and it beats waiting an hour for their last spot to age out looking exactly like somebody still calling. A spot marked Test is somebody checking their equipment, and is left out unless ham-bota-include-tests says otherwise.

One activation can count for several bunkers at once, so the first reference is named and the rest are counted: B/G-0123 Pillbox Hill (+1).

The mode a spot shows

A cluster spot is a callsign and a frequency. It almost never carries a mode, and the operator is still expected to know that 14074 means FT8 — which is exactly the sort of knowing a program can do. ham-band-plan holds which segment of each band is used for what, and the narrowest matching segment wins, so 14074 reports as FT8 rather than merely DATA. A comment naming a mode beats the guess, since that is a person saying what they actually heard.

Tuning sends the rig's own name for the mode, through ham-rig-mode-aliases: a spot saying FT8 puts a modern radio in PKTUSB, and one with no data mode in plain sideband, which is how FT8 worked before the radios grew a setting for it. SSB resolves to LSB or USB by frequency. A mode the rig does not have leaves the mode alone rather than being sent and refused, and ham-spot-qsy-sets-mode turns the whole thing off.

Tuning also clears RIT, XIT and split first. All three put the radio somewhere other than the frequency it was just sent, and the panel would go on reading the right number the whole time. ham-rig-tune-clears-offsets turns that off.

Writing another source

ham-spot.el holds the record, the list, the panel and the radio. A source supplies spots and says how to start and stop itself:

(ham-spot-register-feed
 (ham-spot-feed-create
  :name 'wwff :title "WWFF"
  :start #'my-wwff-start :stop #'my-wwff-stop
  :live-p #'my-wwff-running-p))

There are two ways to deliver spots, and which one a network is decides the rest. A network that pushes — a cluster, sending one line at a time — hands each spot to ham-spot-record, which de-duplicates it, ages it and publishes it:

(ham-spot-record
 (ham-spot-fill-mode
  (ham-spot-create :call "K6SM" :hz 14074000 :when (current-time)
                   :source 'wwff :reference "KFF-1234")))

Two reports of the same station on the same band are one spot, newest winning, so the list holds stations rather than the history of everyone who heard them.

A network that is asked — an API answering with the whole current list — calls ham-spot-replace-feed instead, which swaps that feed's spots wholesale so departures register as well as arrivals. ham-spot-poller-create does the asking: give it a URL, a function to read one answer, and an interval, and it handles the timer, the one-request-at-a-time rule, the service's own minimum interval, and the error reporting. ham-pota.el is about a hundred lines because of it, and is the one to copy.

ham-fetch-json in ham.el is underneath both: curl when it is on PATH and url.el when it is not, asynchronous either way, on every platform.

A network that streams — WWBOTA, holding a request open and pushing events — uses ham-connection directly, with :tls t and an :on-chunk handler that gets the raw text rather than lines. ham-bota.el is the one to copy for that, and it is longer than the polled back ends for the reason its own section explains.

Not yet done

PSK Reporter and DXpeditions are the remaining sources. Canadian Parks on the Air needs no back end — it is POTA's VE references, reachable through ham-pota-programs.

Note that the HamClock family gets its park and summit spots from an aggregation server of its own rather than from the programs: one file that every clock downloads, built server-side. There is no such server here, so each programme's API is read directly — which is why these were separate pieces of work rather than one, and why the units and timestamp quirks above had to be found one network at a time.

Space weather

M-x ham-spacewx opens the panel. Nothing needs configuring first; it reads the public feeds published by the NOAA Space Weather Prediction Center.

The second line carries the NOAA scales — radio blackout, radiation storm and geomagnetic storm — as they stand now and as they peaked over the last 24 hours. A storm that has already passed still shaped the day's propagation, so both matter.

The propagation estimate comes first, because it is the question the rest of the panel is evidence for. The indices behind it follow.

The panel lays itself out to fifty columns — ham-spacewx-panel-width — so it can sit in a side window beside the log or the rig panel without wrapping:

Propagation for grid CM98jr
  MUF, 3000 km hop (MHz)          26.4
  MUF in 12 h (MHz)               9.7
  Absorption floor (MHz)          6.4
  Bands by day
    160m 80m 40m 30m 20m 17m 15m 12m 10m 6m
  Bands at night
    160m 80m 40m 30m 20m 17m 15m 12m 10m 6m

Solar wind (DSCOVR)
  Speed (km/s, 6 h, 250–800)
    ~~~~~~~------------~~~~~~~~=  527
  Bz (nT, 15 h, ±20)
    ~~~~-------~~~~======++++++=  5.0 north

A reading takes two lines. The first names it and gives, in one parenthesis, everything about it that does not change between refreshes: its unit, how long its trace covers, and the scale that trace is drawn to. The second draws the trace and puts the number at the end of it, so every value in the panel lines up on the right hand end of its own sparkline — which is where the eye already is. The trace says what has been happening; the number finishes the sentence with what is happening now, and the words after it say what that means.

Splitting them that way means the eye returning to the panel lands on what moved. A unit and a scale do not change; the number does.

Sparklines are one width across the panel whatever the feed's cadence, so rows covering the same period line up column for column. When a feed has aged out, its age replaces the scale in the parenthesis: a number you cannot date matters more than the height of its ramp. Bz is drawn about zero, since its sign is the whole point, and reports its half height instead — a trace sitting low spent that window southward.

Values, traces and the words beside them share one set of colours. Readings that feed a NOAA scale are coloured on that scale, so the panel and the published alert level agree. The rest use the same palette to mean quiet, degraded and serious.

Two things sit outside that palette. What is in parentheses wears the same quiet face as the panel's opening lines, because it is context rather than content and should not compete with the readings. The propagation figures wear a colour of their own, because nothing measured them: they are worked out from the indices above them, and the colour says so on every row without a word of caveat on any of them. An age in parentheses is the exception to the first rule — a reading that has stopped being current should not read as quietly as one that has not.

Key Action
g Read any feed whose data has passed its own interval
G Read every feed now
w Show the exact solar wind record in use
c Discard stored readings
? Explain the panel

The same commands are on the Space Wx menu.

When a feed fails

A failed read keeps the last good reading rather than blanking the row, and says how old it is. Stale space weather is worth seeing as long as the panel is honest about its age; an empty panel tells you nothing. Any reading past ham-spacewx-stale-after carries its age, whatever the reason.

A machine resuming from sleep finds every feed timing out at once. The panel notices the gap, waits for the network, and retries once.

A feed can also arrive whole and carry nothing. GOES publishes a flux of exactly zero for every record while its X-ray instrument is down, rather than omitting them, and zero is not a quiet sun — the long band sits near 1e-8 at solar minimum and cannot physically reach zero. Drawn as data those zeros make a flat trace along the bottom of the ramp, in the colour of a quiet reading: a picture of six calm hours that were never observed. They are dropped instead, and the panel says no data: every flux reads zero. ham-spacewx-xray-floor sets where a measurement stops counting as one.

M-x ham-spacewx-diagnose re-reads every feed and reports what each answered: the fields it carries, how many records survive being narrowed to one energy channel, how many of those carry a measurement, and the current value of every reading taken from it. The report updates itself as the feeds land. It is the first thing to run when a row is empty, because it separates a moved endpoint from a network problem from an instrument outage from a payload this package does not understand.

Solar wind

The real time solar wind files carry every reporting spacecraft in one document, interleaved and not in time order. The panel sorts by timestamp, narrows to a single spacecraft, and drops samples the feed grades as poor — reading a mixture of DSCOVR and ACE is meaningless, since they sit at different points and are calibrated separately.

w shows the exact record in use: spacecraft, timestamp and quality grade. Put it beside NOAA's own plot when the two disagree.

ham-spacewx-wind-spacecraft follows SWPC's active flag by default, or pins to one spacecraft so you can compare like with like.

Propagation estimate

Set ham-station-grid to your locator and the panel opens with an estimated maximum usable frequency for a 3000 km hop now and twelve hours out, an absorption floor, and per-band summaries for day and night — the evening's bands being the thing worth planning around. Now and twelve hours out are two rows rather than one row and a parenthesis, so they share a column and can be compared by looking down it.

The section is headed with the locator it was worked out for — Propagation for grid CM98jr — because whose ionosphere this is matters more than a reminder that it is modelled. That caveat is below, and in the help.

This is a model, not a measurement. It predicts the ionosphere from solar and geomagnetic indices; an ionosonde network measures it directly. Where the two disagree the ionosonde is right. The chain is the standard one: solar ultraviolet ionises the F2 layer, so the critical frequency follows the solar zenith angle and the level of solar activity; multiplying by an obliquity factor gives the maximum usable frequency; the D layer, lit by the same sunlight and flooded by flare X-rays, absorbs rather than refracts and sets a floor. A band is open between the two. Geomagnetic storms depress the F2 layer in proportion to geomagnetic latitude, which is why a storm closes paths from Alaska while leaving equatorial ones alone. The F2 maximum lags local noon by a couple of hours, because recombination at that height is slow.

The parameterisation follows OpenHamClock (MIT), so this panel and the figures published elsewhere agree rather than differing by several MHz for no visible reason. Every coefficient is a defcustom, so the model can be corrected against real soundings rather than argued about.

Configuration

M-x customize-group RET ham-rig, RET ham-spacewx, and RET ham.

Option Default Meaning
ham-rig-host ham-rig-port localhost 4532 Where rigctld listens
ham-rig-fast-interval 0.2 Seconds between frequency, PTT and meter polls
ham-rig-slow-interval 1.0 Seconds between mode, VFO, split and other-VFO polls
ham-rig-show-other-vfo auto Show the other VFO: when readable directly, always, or never
ham-rig-panel-indicators preamp, ATT, roofing, AGC Settings shown on the rig panel
ham-rig-indicator-poll-batch 2 Indicators read on each slow poll
ham-rig-manufacturer-value-labels Yaesu preamp Front-panel names for values, by manufacturer
ham-rig-model-control-values FTDX-10 roofing filters A control's positions on a model, replacing Hamlib's
ham-rig-model-raw-functions FTDX-10 processor Switches read and set with raw CAT commands on a model
ham-rig-tx-timeout 180 Watchdog unkey when the duration is unknown
ham-rig-tx-timeout-max 600 Ceiling on the watchdog, whatever is declared
ham-rig-tx-watchdog-margin 1.25 Allowance over a declared duration
ham-rig-unkey-attempts 3 Unkey commands before warning the operator
ham-rig-atu-timeout 20 Expected length of a tuner cycle, in seconds
ham-remote-host radio.local Machine at the radio end
ham-remote-transport trx Which back end carries the audio
ham-remote-mumble-run client Which halves of Mumble this machine runs
ham-remote-mumble-follow-ptt t Microphone open only while keyed
ham-remote-mumble-user login name Name to join the server under
ham-remote-mumble-channel nil Channel to join, or the default
ham-remote-mumble-port 64738 Mumble's registered port
ham-remote-mumble-bandwidth 72000 Per-client ceiling, bits per second
ham-remote-mumble-users 4 Slots the server admits
ham-remote-require-lossless nil Refuse a back end that compresses
ham-rig-meter-ranges VDD, ID, comp Range to draw a meter over
ham-rig-meter-zones SWR, ALC Where a bar turns amber and red
ham-remote-settle-delay 0.2 Seconds from keying to audio being worth sending
ham-rig-tuning-steps 1 Hz … 1 MHz Steps ← and → cycle
ham-rig-tx-meters power, ALC, comp, SWR, VDD, ID Meters shown while keyed
ham-rig-meter-units comp, VDD, ID Full scale for normalised meters
ham-rig-meter-zones SWR at 2 and 3 Where a meter turns amber and red
ham-rig-controls-exclude nil Controls to omit
ham-rig-percent-step 0.01 Smallest change to a normalised level
ham-rig-passband-ranges per mode Filter width range and step, as (MODE MIN MAX STEP)
ham-rig-poll-when-hidden nil Keep polling with no panel visible
ham-rig-mode-aliases FT8 → PKTUSB, … What a spot's mode is called on the radio
ham-rig-tune-clears-offsets t Clear RIT, XIT and split when tuning to a spot
ham-frequency-format dotted 14.074.000, khz or mhz
ham-band-default-frequencies digital calling Where b moves on each band
ham-band-plan per band Which segment of a band is used for which mode
ham-station-grid nil Your Maidenhead locator
ham-dxcluster-call nil Callsign to log in to the cluster with
ham-dxcluster-host, -port nc7j, 7373 Which cluster to connect to
ham-dxcluster-commands nil Filters to send once logged in
ham-dxcluster-backlog-count 30 Recent spots to ask for on connecting
ham-spot-max-age 60 Minutes a spot is held, for every panel
ham-spot-max-spots 500 Most spots to hold, however recent
ham-spot-sort age A new panel's order: age, frequency, call, source
ham-spot-filter nil A new panel's "show only" regexp
ham-spot-exclude nil A new panel's "hide" regexp
ham-spot-modes nil A new panel's modes: ("SSB" "CW")
ham-spot-bands nil A new panel's bands
ham-spot-mode-groups SSB, CW, DATA… Which modes each family name covers
ham-spot-qsy-sets-mode t Whether tuning to a spot sets the mode too
ham-spot-separate-buffers nil Give each feed its own window
ham-pota-programs nil Park programs by reference prefix: K, VE
ham-pota-include-rbn t Include POTA's Reverse Beacon Network spots
ham-pota-interval 120 Seconds between reads of POTA
ham-sota-host api-db2 The SOTA API; not the retired api2
ham-sota-associations nil SOTA associations to show
ham-sota-window 120 Minutes of SOTA spots to ask for
ham-wwff-programs nil WWFF national programmes: KFF, DLFF
ham-wwff-interval 120 Seconds between reads of WWFF
ham-bota-programs nil WWBOTA programmes, without the B/: G, DL
ham-bota-backlog-hours 1 Hours of history the stream sends on opening
ham-bota-include-tests nil Show spots marked as an equipment test
ham-fetch-backend auto curl subprocess, or Emacs's url.el
ham-spacewx-fetch-backend auto curl subprocess, or Emacs's url.el
ham-spacewx-panel-width 50 Columns the panel lays itself out to
ham-spacewx-metric-views per reading Window and width of each sparkline
ham-spacewx-noaa-scales R, S, G Thresholds each scale's levels begin at
ham-spacewx-severity-thresholds per reading Where an unscaled reading turns
ham-spacewx-wind-spacecraft active Which spacecraft the wind comes from
ham-spacewx-wind-speed-field proton Proton or alpha particle speed
ham-spacewx-max-quality 0 Strictness of the feed's own grading
ham-spacewx-stale-after 3600 When a reading is called old
ham-spacewx-xray-floor 1e-9 Below this a flux is a gap, not a reading
ham-spacewx-xray-long-band-regexp 0.1-0.8 How the long band is spelled
ham-spacewx-auto-refresh-interval 600 Seconds between refreshes
ham-spacewx-obliquity-factor 3.2 M(3000)F2, turning foF2 into a MUF
ham-spacewx-fof2-noon-per-sfi 0.04 foF2 rise per solar flux unit
ham-spacewx-f2-peak-hour 14.0 Local hour of the F2 maximum
ham-spacewx-bands 160m–6m Bands the estimate reports on

Polling stops when no panel is visible and nothing has subscribed.

M-x ham-rig-show-stats reports request count, timeouts, discarded replies, errors, mean and maximum round-trip latency, and queue depth. Check it on a new radio: rising latency or a queue that will not drain means ham-rig-fast-interval is too short for the CAT rate.

Following the radio from your own code

(ham-subscribe ham-rig-topic-frequency 'my-logger
               (lambda (hz)
                 (message "now on %s" (ham-format-frequency hz))))
Topic Arguments
ham-rig-topic-frequency frequency in Hz
ham-rig-topic-mode mode string, passband in Hz
ham-rig-topic-ptt t or nil
ham-rig-topic-vfo VFO name
ham-rig-topic-split t or nil
ham-rig-topic-connection state symbol, detail string

Events fire on change, not on every poll. Subscribing again with the same id replaces the handler. Handlers run synchronously from a process filter and must not block; one that signals an error cannot affect the others.

To read state directly: ham-rig-frequency, ham-rig-current-mode, ham-rig-other-vfo, ham-rig-ptt-p, ham-rig-connected-p, ham-rig-power-state, and ham-rig-get for the rest.

ham-spacewx publishes on ham-spacewx-updated with the source key and its payload, and offers ham-spacewx-kp, ham-spacewx-solar-flux, ham-spacewx-sunspot-number, ham-spacewx-xray-flux, ham-spacewx-proton-flux, ham-spacewx-solar-wind-speed, ham-spacewx-bz, ham-spacewx-scale-now, ham-spacewx-muf and ham-spacewx-band-condition.

ham.el also offers ham-maidenhead-to-latlon and ham-latlon-to-maidenhead (4, 6 or 8 characters), ham-great-circle and ham-grid-distance returning distance and bearing, ham-band-for-frequency, ham-parse-frequency and ham-format-frequency.

Credits

Two separate projects, easily confused, helped here and are credited apart.

OpenHamClock supplied the propagation estimate's parameterisation, and its useWWBOTASpots hook is where the WWBOTA endpoint and the shape of a bunker spot came from — including that the stream is Server-Sent Events and that a QRT spot means somebody has finished.

HamClock supplied the DX cluster handling: how to tell the three families of cluster software apart, what to ask each one for a backlog, and the detail that the login prompt arrives with no newline after it. Its dxcluster.cpp has had far more hours on real clusters than this has. The band segment table follows the one the DX cluster software publishes in its bands.pl, corrected where the published tables lag practice: 30 metre FT8 is at 10136, not 10131.

Activation spots come from Parks on the Air, Summits on the Air, World Wide Flora and Fauna and Worldwide Bunkers on the Air, whose spotting APIs are public and read-only. The field names and units of each were checked against working clients rather than guessed at, because no two of them agree.

Space weather data comes from the NOAA Space Weather Prediction Center, a work of the United States government and in the public domain.

License

GPL-3.0-or-later.

About

Amateur Radio support for Emacs

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages