Add CW keying, function key messages and the documentation

The function keys send through cwdaemon or a WinKeyer, with N1MM's message
macros. Escape stops sending.

Settings are read with the reflection serializer rather than a generated one:
the generated one hands back null for every property the file leaves out
instead of the value the property is declared with, which crashed the program
the first time a new setting was added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik
2026-08-27 11:23:30 +00:00
parent d96cbe146b
commit f5aa77ece3
25 changed files with 1002 additions and 26 deletions

141
README.md Normal file
View File

@@ -0,0 +1,141 @@
# Nonemm
A contest logger for amateur radio: a from-scratch reimplementation of
[N1MM Logger+](https://n1mmwp.hamdocs.com/) in C#, running on Linux and Windows.
The contest rules, log formats and database schema are written from their
published definitions. What Nonemm keeps is **interoperability**: the log
database is N1MM's `.s3db` in N1MM's schema, user-defined contests are N1MM's
`.udc` files, and a log either program writes opens in the other.
## Running it
The SDK lives at `~/.dotnet` on this machine, so a shell that has not been set
up needs:
```sh
export DOTNET_ROOT=$HOME/.dotnet PATH="$HOME/.dotnet:$PATH"
```
Then:
```sh
dotnet run --project src/Nonemm.App # start the logger
dotnet test Nonemm.slnx # run every test
```
`./build.sh` does the same with the environment already set: `./build.sh test
Nonemm.slnx`.
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.
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
scorer: red for a dupe, green for a new multiplier, blue for points.
## What it does
| | |
|---|---|
| Contests | CQ WW, CQ WPX, 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 |
| Windows | entry, log, check, bandmap, score summary, packet |
| Radio | hamlib `rigctld`, reconnecting on its own |
| Cluster | DX cluster over telnet, spots feeding the bandmap |
| 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 |
### The country file and the callsign database
Neither is bundled. **Config → Download Country File** and **Download Check
Partial File** fetch them from where they are published, which is where N1MM
fetches them from too —
[`country-files.com/cty/wl_cty.dat`](https://www.country-files.com/cty/wl_cty.dat)
and
[`supercheckpartial.com/MASTER.SCP`](https://www.supercheckpartial.com/MASTER.SCP).
The country file is `wl_cty.dat` rather than plain `cty.dat`: same format, with
the WAE entities listed separately, which is what CQ WW counts.
A download that fails changes nothing. The file is fetched, checked that it
parses as what it claims to be, and only then put in place, so a site that
answers with an apology page instead of a country file cannot cost an operator
their multipliers mid-contest.
Both files can also be dropped into `SupportFiles` under the configuration
directory (`~/.config/nonemm` on Linux, `Documents\Nonemm` on Windows).
User-defined contests go in `UserDefinedContests` under the same directory.
Without a country file the program still runs; country- and continent-scored
contests lose accuracy.
### Radio control
The logger reads and tunes the radio through
[hamlib](https://hamlib.github.io/)'s `rigctld`, started separately for whichever
radio is on the desk:
```sh
rigctld -m 2028 -r /dev/ttyUSB0 # -m is the hamlib model number; rigctl -l lists them
```
Then **Config → Radio**. With no radio connected nothing changes: frequency and
mode stay where they were last typed.
### CW
**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.
### Networked stations
**Config → Network** names this station and lists the others. Each contact is
sent to them as it is logged, in N1MM's `contactinfo` message, so an N1MM
station on the same network sees them too. With no addresses listed the contacts
are broadcast. A contact that arrives is scored again here from the rules rather
than trusted.
## Layout
| Project | What it holds |
|---|---|
| `Nonemm.Core` | Frequencies, bands, modes, callsigns, grid squares, the country file and the callsign database |
| `Nonemm.Contests` | Contest rules, the scoring engine, `.udc` files |
| `Nonemm.Formats` | Cabrillo out, ADIF in and out |
| `Nonemm.Storage` | The N1MM-compatible `.s3db` |
| `Nonemm.Rig` | Radio control over `rigctld` |
| `Nonemm.Spotting` | Spots, the bandmap, the DX cluster client |
| `Nonemm.Network` | Contacts shared between the stations of a multi-operator entry |
| `Nonemm.Keying` | CW through `cwdaemon` or a WinKeyer |
| `Nonemm.Session` | What the operator is typing and what the log says about it — no UI toolkit |
| `Nonemm.App` | The Avalonia windows |
The split at `Nonemm.Session` is the important one: it references no UI
framework, so what space does, when a dupe fires and what a contact scores are
covered by plain unit tests.
## Where it stands
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, bandmap, score and packet windows, radio control, DX cluster
spots, contacts shared between networked stations, and CW keying.
Checked against N1MM 1.0.11031: a log this program wrote opens in N1MM, which
reads the contest, its categories and the contacts. See
[`docs/n1mm-interop.md`](docs/n1mm-interop.md) for what that took.
Not yet: voice keying, QTC handling for WAE, call history files, digital modes
beyond logging them, and the check window's Call History and Exchange columns,
which are left out rather than shown empty.
The radio, cluster, network and keyer clients are tested against fakes that
speak the documented protocols. None has yet been run against a real radio, a
live cluster node or a keyer.