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. |
- Emacs 29.1 or later
- Hamlib, for
rigctld curl, optional, forham-spacewx— standard on macOS and Linux, and shipped with Windows since 10/1803. Without it the panel falls back to Emacs's ownurl.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.trxandzita-njbridgeare 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.
(add-to-list 'load-path "~/.emacs.d/lisp/ham/")
(require 'ham-rig)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.
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 |
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").
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.
↑ 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.
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-zonessets 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-rangessets 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.
- A watchdog unkeys the radio if a transmission runs too long.
- Unkeying is confirmed, not assumed. After commanding the radio off,
ham-rigreads PTT back. If the radio still reports itself transmitting it commands the unkey again, up toham-rig-unkey-attemptstimes, 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.
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.
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, notAMP1. Yaesu's words are built in (ham-rig-manufacturer-value-labels); supply your radio's own withham-rig-control-value-labelsandham-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.
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 ?;.
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.
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.
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.
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.
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.
| 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.
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.
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.
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.
- At your desk,
M-x ham-remoteands. The panel showsMumble running. - You should hear the band. If not, the problem is at the radio end's input.
M-x ham-rig, connect, and key witht. The panel'sMICshould flip fromshuttoopen, and the radio should transmit your voice.- Unkey.
MICgoes back toshut.
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.
| 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.
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 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.
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 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.
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.
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 |
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.
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.
(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.
(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 AirNothing 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 skimmersCanadian 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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
(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.
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.
GPL-3.0-or-later.