Give the telnet window the rest of what N1MM's has

The packet window showed traffic and took a command line, and everything else
about the cluster lived in a dialog under Config. It is now the window N1MM has,
with the same five tabs.

Telnet shows the traffic with spot lines in green, what went out in blue and
lines from a preferred spotter in bold. Double clicking a spot line, or "Jump to
this spot", puts the radio there with the call in the entry window. Scrolling
stops while the pointer is over the traffic. The client keeps the last two
hundred lines, so a window opened mid-contest is not blank.

Clusters keeps the operator's nodes with their ports, passwords and after-login
commands, connects and disconnects, and holds the logon settings. Download
fetches the published list of telnet nodes from NG3K — around fifty, with the
sysop's call and a note about each — and clicking one fills the boxes in. N1MM
downloads its list from its own web service, which asks the operator to opt in
to data collection and is N1MM's to run, so this reads a public page instead.
ClusterList takes any page with telnet:// links in a table; a page it cannot
read leaves the stored list alone.

Filters decide which spots reach the bandmap: bands, modes, beacons, busted
calls, stations outside the call history file, blacklisted spotters and calls,
spots from outside your country, continent or a list of prefixes, and how long a
spot stays on the map. A busted spot is a call the callsign database has never
heard that is one character away from one it knows; a call nothing resembles is
kept, because that is what a new station looks like. Nothing is filtered out of
the traffic itself — the operator sees everything the node sends.

Buttons edits the twelve command buttons. A button takes what N1MM's takes: the
message macros, several commands separated by semicolons, or {CONN} and the name
of a favourite, which connects to that node instead of sending anything. The
label takes the macros too. Right-clicking a button opens the editor.

Config ▸ Cluster now opens this window on the Clusters tab rather than a dialog
of its own, which is where N1MM keeps those settings.

Three things that could take the program down while a cluster was connected:

Settings.Load read a null where the property is not nullable. A file that names
a key with a null value — one written before the property existed and then
edited — put that null straight through, because the property's own default only
runs when the key is missing. Opening the telnet window then threw on the first
list it touched. Every null is now put back to the default the property
declares, walking into the stored records and the lists of them.

Bandmap was written from the cluster's thread and read from the window's, so a
dictionary could be modified while a window enumerated it. Every method locks
now.

ClusterClient disposed its token source while its own loop still used it, and
the retry delay sat outside the catch, so a disconnect faulted the loop task.

Along the way the message macros were checked against N1MM's function-key
documentation, and several were wrong. {LOGGEDCALL} is N1MM's {LASTCALL}, the
serial is #, and there is no {MYZONE}; {NAME} and {GRIDSQUARE} stand for the
other station's name and grid, not ours; {OTHERMHZ} is the radio the operator is
not on. The single-character macros * and ! were missing. Added from the same
table: {LASTCALL}, {PREVNR}, {NAMEANDSPACE}, {CHNAME}, {GRID}, the two grid
bearings and the grid distance, {FREQ}, {FREQROUND}, the other-radio
frequencies, {TIMESTAMP} and {TIME2}. Frequencies are formatted the way N1MM
formats them, with R for the decimal point on CW. The macros that pass a station
to the other band take the second radio as a new argument, and stand for nothing
at a one-radio station.

Left out, and written down: saving spots to a database, which N1MM keeps in its
admin database rather than in the log file the two programs share; the
special-calls list; the two-character busted check, which is a few hundred
thousand lookups per spot against a few hundred for one character; and N1MM's
action macros, which need a different shape than an expander that returns a
string.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 07:33:55 +00:00
parent 648da1918a
commit d9f9d880f2
40 changed files with 2278 additions and 272 deletions

102
README.md
View File

@@ -31,7 +31,7 @@ To work a contest: **Config → Station** for your callsign and zones, then
**File → New Database** and **File → New Contest**. In the entry window, type a
callsign and press **space** to move to the exchange, then **Enter** to log.
Type a frequency into the callsign box and press Enter to change band. **View**
opens the log, check, bandmap, score and packet windows.
opens the log, check, bandmap, score and telnet windows.
Everything that judges a station — the bar under the callsign box, a row in the
check window, a spot on the bandmap, a line in the log — is coloured by the same
@@ -44,10 +44,10 @@ scorer: red for a dupe, green for a new multiplier, blue for points.
| Contests | CQ WW (CW, SSB, RTTY), CQ WPX (CW, SSB, RTTY), WAE (CW, SSB, RTTY), ARRL DX, IARU HF, Sweepstakes, RTTY Roundup, NAQP, general logging, and user-defined `.udc` contests |
| Log | N1MM `.s3db`, Cabrillo 3.0 out, ADIF in and out |
| While typing | dupe check, multiplier check, points, country and zone from the country file, exchange filled from a call history file |
| Windows | entry, log, check, bandmap, available mults and Qs, score summary, packet |
| Windows | entry, log, check, bandmap, available mults and Qs, score summary, telnet |
| Editing | double-click a cell in the log, or open the whole contact with Ctrl+Y; Delete removes it. All of it goes out to the other stations |
| Radio | one or two radios over hamlib `rigctld`, split, reconnecting on its own |
| Cluster | DX cluster over telnet, spots feeding the bandmap, Alt+P to spot a station |
| Cluster | DX cluster over telnet: a telnet window with the node's traffic, the published list of nodes, command buttons and spot filters, spots feeding the bandmap, Alt+P to spot a station |
| Bandmap | drawn like N1MM's: a frequency scale with the receiver on it and callsigns beside it, joined by leader lines |
| Network | contacts shared with the other stations of a multi-operator entry, in N1MM's own contact message |
| Keying | CW through `cwdaemon` or a WinKeyer, with N1MM's message macros |
@@ -92,9 +92,30 @@ mode stay where they were last typed.
**Config → Keyer and messages** picks `cwdaemon` (a UDP port, usually 6789) or a
WinKeyer (a serial port), sets the speed, and edits the twelve function key
messages for CW and for phone. The macros are N1MM's: `{MYCALL}`, `{CALL}`,
`{EXCH}`, `{SENTRST}`, `{SENTNR}`, `#` for the serial number, and `{SENTRSTCUT}`
for cut numbers. Escape stops sending.
messages for CW and for phone. Escape stops sending.
The macros are N1MM's, spelled the way N1MM's function-key documentation spells
them, so a `.mc` file written for either program says the same thing. The text
macros that are filled in:
| | |
|---|---|
| `*`, `{MYCALL}` | your callsign |
| `!`, `{CALL}` | the call being worked, or the last one logged when the box is empty |
| `#` | the serial number for this contact, or the last one when the box is empty |
| `{LASTCALL}`, `{PREVNR}` | the call and the serial of the contact just logged |
| `{EXCH}` | the sent exchange from the contest setup |
| `{SENTRST}`, `{SENTRSTCUT}` | the report you send, plain or in cut numbers |
| `{NAME}`, `{NAMEANDSPACE}`, `{CHNAME}` | the other operator's name, from the entry window or the call history file |
| `{GRID}` / `{GRIDSQUARE}` | your grid / theirs |
| `{GRIDBEARING}`, `{REVGRIDBEARING}`, `{KMGRIDDISTANCE}` | bearing and distance between the two |
| `{FREQ}`, `{FREQROUND}` | this radio's frequency in kilohertz, with `R` for the decimal point on CW |
| `{OTHERFREQ}`, `{OTHERFREQROUND}`, `{OTHERMHZ}`, `{OTHERBAND}`, `{LRMHZ}`, `{RRMHZ}` | the other radio, for passing a station |
| `{TIMESTAMP}`, `{TIME2}` | the time now |
N1MM's action macros — `{WIPE}`, `{LOG}`, `{RUN}`, the CAT and SO2R families —
are not run; they stand for nothing, so a message that holds one still sends the
right characters.
### Editing the log
@@ -318,9 +339,57 @@ always 0, so 2190M and 630M are told apart by their edges alone.
### The DX cluster
**Config → Cluster** takes the node's address, a password for the few nodes that
ask for one, and the commands to send after login. Filters are the node's
business, so whatever goes in the command box is sent as typed and left alone.
**View → Telnet**, or **Config → Cluster**, opens the telnet window. It has four
tabs, the ones N1MM has:
- **Telnet** shows the node's traffic as it arrived, spot lines in green and
what went out in blue. Type a command at the bottom, or press one of the
buttons; up and down walk back through what has been typed. Double clicking a
spot line, or "Jump to this spot" on the right-click menu, puts the radio
there with the call in the entry window. Scrolling stops while the pointer is
over the traffic, so a line can be read while the node keeps sending.
- **Clusters** keeps a list of nodes with their names, ports, passwords and the
commands to send after login. Connect and Disconnect are there, with automatic
logon, the call to log on with, and the keep-alive interval. **Download** under
it fetches the published list of nodes from
[NG3K](https://www.ng3k.com/misc/cluster.html) — around fifty nodes with the
sysop's call, the address and a note about each — and clicking one fills the
boxes in. N1MM downloads its list from its own web service, which asks the
operator to opt in to data collection; this reads a public page instead.
- **Filters** says which spots reach the bandmap: bands, modes, beacons, busted
calls, stations outside the call history file, blacklisted spotters and calls,
whether to take spots only from your country, your continent or a list of
prefixes, and how long a spot stays on the bandmap. It also holds the preferred
spotters and the switch that randomises incoming CW spot frequencies. Nothing
is filtered out of the traffic itself; the operator sees everything the node
sends.
- **Buttons** edits the twelve command buttons: a label and its commands, or
back to the defaults.
- **Spot comment** is what goes out with the spots you send.
The buttons come from the settings; without any, they are `sh/dx`, `sh/dx/20`,
`sh/wwv`, `sh/users`, `sh/c/n`, `help` and `bye`. A button holds what N1MM's
buttons hold: several commands separated by semicolons, the function-key message
macros — `sh/dx {MYCALL}` sends the station callsign — or `{CONN}` and the name
of a favourite, which connects to that node instead of sending anything. The
label takes the macros too, expanded once when the buttons load, so a label of
`{MYCALL}` reads as the callsign. Right-clicking a button opens the editor, and
its tooltip shows the command behind it. What is typed on the command line goes
out as typed, again as N1MM sends it. The one place this differs from N1MM: the
commands of one button go out together rather than a quarter of a second apart.
Three of the filters are worth spelling out:
- **Busted spots.** A spotted call the callsign database has never heard, but
which is one character away from a call it does know, is a miscopy and is
dropped. A call nothing resembles is kept, because that is what a new station
looks like. Without `MASTER.SCP` nothing is called busted.
- **Preferred spotters.** Spot lines from those spotters are written in bold, so
the spotters worth believing stand out. They are matched on the start of the
call, so `W3LPL` also covers `W3LPL-#`. Nothing is filtered by this.
- **Randomised frequencies.** Each incoming CW spot is moved thirty or sixty
hertz either way, which is what N1MM does, so the operator has to find the
station by ear. Phone and digital spots are left alone.
The client speaks telnet properly: it answers the option negotiation instead of
letting the control bytes turn up in the first lines of text, and it reads the
@@ -328,12 +397,17 @@ login prompt out of a partial line, because nodes write `login: ` with no line
ending. If no prompt arrives within ten seconds the callsign goes out anyway,
which is what N1MM does. A connection that has heard nothing for four minutes
gets a blank line, so the node does not drop it as idle. A dropped connection is
retried.
retried. The last two hundred lines are kept, so a window opened mid-contest is
not blank.
Alt+P, or **Edit → Spot It**, puts the call being typed on the cluster at the
current frequency; with nothing typed it spots the last contact logged. The spot
also goes straight onto our own bandmap rather than waiting to come back round
from the node.
current frequency, with the comment from the Spot comment tab — which takes the
message macros too; with nothing
typed it spots the last contact logged. The spot also goes straight onto our own
bandmap rather than waiting to come back round from the node.
Filters are applied to the spots, not to the node: it is still worth filtering
at the node as well, because that is less traffic on the wire.
### Networked stations
@@ -371,7 +445,7 @@ covered by plain unit tests.
Working: logging a contest end to end, live dupe and multiplier checking, eight
built-in contests plus user-defined ones, Cabrillo and ADIF export, ADIF import,
the log, check, graphical bandmap, score and packet windows, editing and deleting logged
the log, check, graphical bandmap, score and telnet windows, editing and deleting logged
contacts, radio control, DX cluster spots, contacts shared between networked
stations, and CW keying.