Three pieces on top of the link: **The network status window**, on the entry window's Window menu. A row per station, this computer included the way N1MM shows it: number, address, operator, band, mode, run, transmit, pass frequency, the last message type, how long ago it arrived, the counts each way and the echo round trip. A station that broadcast the wrong version is named under the table in red — that is the one fault where everything looks connected and nothing arrives. The box at the bottom sends a line of chat, and Echo asks every station whether it is there. **Where this station is** goes out once a second when it has changed. The frequency, the mode and run have a dozen places they can change from — the radio moving, a band button, a QSY typed into the callsign box — so it is read and compared rather than announced from each of them. **The ten-minute rule.** `BandChangeRules` already counted changes and the stay on a band, and the entry window already showed the countdown, but only a user-defined contest carried a rule, so every built-in contest allowed anything. `ForCategory` gives a multi-operator entry with one or two transmitters a ten-minute stay, which is N1MM's fallback in `ContestInstance.BandChangeTimerDuration` for every contest that does not name its own. Its per-contest table is a few hundred cases in a decompiled hash switch and is not repeated. Config ▸ Edit Networked-Computer Names now covers both networks: the 12060 broadcast to other programs and the 12070 link, with the station number, the port, the version to claim and stations named by address for a network where a broadcast does not reach. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdAYHcdRktqKry7nk414TU
571 lines
35 KiB
Markdown
571 lines
35 KiB
Markdown
# What is not finished, and what has not been tested
|
|
|
|
Two separate lists. A feature can be finished and untested, or half-built and
|
|
exercised every day. Written 2026-08-27, updated 2026-08-31.
|
|
|
|
## Never run against the real thing
|
|
|
|
Everything below is written from a published protocol or from N1MM's source and
|
|
tested against a fake that speaks the same protocol. A fake agrees with whatever
|
|
the person who wrote it believed, so these are the places where a wrong belief
|
|
is still sitting there undiscovered.
|
|
|
|
| What | Tested against | What has never happened |
|
|
|---|---|---|
|
|
| `RigctldRadio` | a stand-in for `rigctld` over a real socket | no real `rigctld`, no real radio. The extended answer format, the `+` prefix and the split commands are from the documented protocol. If the format is wrong, nothing works rather than something being subtly off — check this first. |
|
|
| `OtrspBox` | a `MemoryStream`, checking the bytes | no real SO2R box. Command forms are from N1MM's `N1MMPort.cs`. |
|
|
| `ClusterClient` | a node fake over a real socket, sending the telnet negotiation, the login prompt and spot lines | no live cluster node. Which nodes send bare CR, and which send option negotiation, is guessed from N1MM's code. |
|
|
| `StationNetwork` | the message format, round-tripped | no second station, and no N1MM on the same network. |
|
|
| `StationLink` | the bytes of every message, and two links talking over loopback | no real N1MM has ever been on the other end. The version check is the thing to try first: N1MM turns away a station whose version is not its own, and this program has to be told what to claim. |
|
|
| `CwDaemonSender` | the UDP messages, and a fake daemon that answers the `<ESC>h` reply request | no `cwdaemon`, no radio keyed. |
|
|
| `MmttyEngine` and the Wine bridge | MMTTY 1.70 under Wine 10 on a machine with a sound card: started, keyed, a message transmitted and decoded back off the air, and stopped | no radio. No FSK through EXTFSK, no PTT on a serial port, and 2Tone has never been run. Keying was wrong until 2026-09-01: `SetMmttyPTT` stops a transmission and does not start one, which is why nothing ever went out before then. `docs/digital-bridge.md` |
|
|
| `WinkeyerSender` | the status-byte reader, on its own | no test of the serial side, and no WinKeyer. The host-mode open sequence is from the WinKeyer datasheet; the status bits are from N1MM's `Winkey.cs`. |
|
|
|
|
The Cabrillo output has not been put in front of a contest sponsor's robot.
|
|
|
|
## Half-built
|
|
|
|
**The digital window: what N1MM has and this does not.** The window is here —
|
|
receive pane in both of N1MM's scroll modes, the Letters/Figs and MouseOver
|
|
readouts, the pause bar, coloured callsigns with N1MM's validity routines, grab
|
|
list, call stacking, hover mode, the message buttons, the engine controls, the
|
|
text file in and out — and the function keys, ESM and the QTC window send
|
|
through the engine on a digital mode. Left out: the waterfall, the spectrum and
|
|
the XY scope, which the engine reports and nothing draws; more than one receive
|
|
pane, which is MMVARI's multi-channel decoding rather than MMTTY's; N1MM's
|
|
AutoTRXUpdate, which offsets the frequency the entry window and the log record
|
|
rather than only the one in the title bar; and N1MM's Send All, which keys a
|
|
whole QTC series at once. The other interfaces N1MM offers — fldigi, MMVARI, the
|
|
hardware TNCs — are not here either, though `DigitalEngine` is the place to add
|
|
them.
|
|
|
|
The receive pane has been driven with a stand-in bridge that prints prepared
|
|
text, which is how the printing, the colouring, the Letters/Figs box and the
|
|
type-ahead transmit pane were checked. Nothing has been decoded from a real
|
|
signal and nothing has gone on the air: this machine has no sound card, and
|
|
MMTTY does not start on it.
|
|
|
|
**The entry window's function keys on a digital mode.** They are the digital
|
|
macros, not the CW file: N1MM loads its send buttons from the RTTYBTN set on a
|
|
digital mode, and the CW messages carry no `{TX}`, so a key pressed there fed
|
|
the engine without keying the transmitter and nothing went out. The first ten
|
|
digital macros are F1 to F10; F11 and F12 stay Spot and Wipe, as in every other
|
|
mode, and the macros past the tenth are on the digital window's own buttons.
|
|
Right-clicking a key still opens the CW or phone messages, so the digital macros
|
|
are edited from the digital window.
|
|
|
|
**How the transmit pane works.** The pane is one editable box holding the whole
|
|
message: what the engine has transmitted, coloured red, then what is still to
|
|
go. The red stops where the air is, not where the feeder is: the last characters
|
|
handed over are still in the engine, and colouring those too turned every
|
|
character red as it was typed once the transmission had caught up. An edit that
|
|
reaches into the red is undone, and so is one into the characters the engine
|
|
holds but has not sent yet: the engine cannot give a character back.
|
|
|
|
The two are one text, so they wrap together and neither takes width from the
|
|
other. A character moving from one half to the other only moves the colour
|
|
boundary, so the caret and what is being typed stay where they are.
|
|
|
|
Nothing goes on the air until the transmitter is keyed, which is the TX button,
|
|
Ctrl+Enter or Alt+T. From then on the pane goes out and so does whatever is
|
|
typed into it, and a function key pressed meanwhile goes on the end of what is
|
|
already waiting. Enter is a new line in the message. The message ends when the
|
|
transmitter has been told to drop and the engine says it has: what went out is
|
|
cleared off the pane, what was typed ahead is kept, and nothing goes out again
|
|
until TX is pressed. A new message clears the pane the same way, for the case
|
|
where two of them run close enough together that the transmitter never drops.
|
|
|
|
`{RX}` runs when the message it stands in has been handed over, wherever it
|
|
stands in the macro. Every other action macro runs before the text unless it
|
|
stands after `{END}`, but the transmitter cannot drop before the text has gone
|
|
out. Running it in place, in front of the text, was what made the first click on
|
|
a CQ button beep for half a second, unkey, then send the rest: the stop was
|
|
issued while the engine held the two characters of the feeder's lead, so MMTTY
|
|
sent them and dropped, and nothing was left to stop the transmitter at the end.
|
|
The bridge log showed `> ptt 1` one millisecond after `> key 1` and before the
|
|
first character. N1MM does the same as this now: it takes `{RX}` out of the text
|
|
wherever it is, sends the text, then calls `StopTX`.
|
|
|
|
The transmitter drops on `{RX}`, on the RX button or on Alt+T, and all three do
|
|
what N1MM does at the end of a message, which is not what it does anywhere else.
|
|
N1MM hands MMTTY the whole message in one `SendString` and calls
|
|
`SetMmttyPTT(1)` with the message still in the engine's buffer. MMTTY then ends
|
|
the transmission itself, at the last character. It never asks MMTTY how much is
|
|
left; `TxBufLen` does not appear in N1MM at all.
|
|
|
|
What sits between the text and the stop there is nothing at two of N1MM's three
|
|
MMTTY send paths (`DigitalInterface.cs:19177` and `:19451`) and `sSleep(400)` at
|
|
the third, the macro buttons (`:21553`). The sleep is not the mechanism — a full
|
|
buffer is — and as a wait it is both too long on a long message and wrong on a
|
|
short one: `TU` is 330 ms of air at 45.45 baud, so 400 ms of sleeping puts the
|
|
stop after the buffer has emptied, which is where it does nothing. This program
|
|
asks instead. `TypeAhead.WhenHoldingAsync` sends the stop as soon as the count
|
|
says the engine holds something, and gives up after N1MM's 400 ms.
|
|
|
|
So `{RX}` stops feeding: `TypeAhead.TakePending` hands over everything that is
|
|
left in one piece, and the stop goes out 400 ms behind it. Nothing after that
|
|
can be rewritten, which is what `{RX}` means. The feeder still paces the middle
|
|
of a message, which is what makes type-ahead work; only the ending is N1MM's.
|
|
|
|
`{RX}` runs as soon as the message has been given to the keyer, not after the
|
|
message has gone out. That is the whole point: waiting for the feeder to hand
|
|
the last character over leaves the engine empty, and a stop that reaches an
|
|
empty engine does nothing. `EntryWindow.SendThenAsync` takes `{RX}` out of what
|
|
stands after `{END}` and runs it there; the rest of `{END}` still waits for the
|
|
message to go out.
|
|
|
|
What the ending costs on the air, from a bridge log of one CQ: the last
|
|
character was fed at 9616 ms and went out at about 10606 ms, and the transmitter
|
|
did not drop until 11549 ms. That is 950 ms of dead carrier per transmission
|
|
paid on the old timer. The key-down fallback brings it to about 230 ms. N1MM's
|
|
ending costs nothing, because MMTTY drops the transmitter on the last character
|
|
itself.
|
|
|
|
The reason is that `SetMmttyPTT(1)` does nothing at an engine that has been fed
|
|
one character at a time and is therefore nearly empty. Probe question 9 sent it
|
|
with `TxBufLen` at 0 and MMTTY transmitted for another eight seconds; a bridge
|
|
log of three CQs shows the same on the air, with the transmitter staying up
|
|
until this program forced it down. Whether it is the empty buffer or the way the
|
|
text arrives that MMTTY objects to is not settled: probe question 11 sent N1MM's
|
|
exact sequence and it did not drop either, but that machine has no sound card,
|
|
so MMTTY never really transmits and its buffer reads 0 whatever it is given.
|
|
|
|
If the engine still does not drop the transmitter, the key goes down here: the
|
|
control's `PTT` property back to false, which took 251 ms in the probe. What it
|
|
waits for is the engine, not a clock: `StopPatience` is how long the engine may
|
|
make no progress, not how long the whole wait may take. Measured against the
|
|
whole wait it cut a CQ off with 21 symbols still in the engine, because a
|
|
flushed message is seconds of transmission and the engine is entitled to all of
|
|
it.
|
|
|
|
Two things had to be right before the flush worked at all, and both were wrong
|
|
first. The flush takes the same turn the feeder takes, so a character the feeder
|
|
had already taken and was still handing over cannot end up behind the rest of
|
|
the message — it did once, and the CQ went out with its first letter at the end.
|
|
And what the engine holds is counted across feeders rather than reset when one
|
|
starts: a feeder that started after a flush forgot a whole message the engine
|
|
was still holding, called the transmission over and put the key down in the
|
|
middle of it.
|
|
`TypeAhead.WhenEmptyAsync` waits for `Aired` first, which is two things at
|
|
once: the engine's own count at 0, and the clock saying the characters the
|
|
engine held have had time to go out. `StopPatience` caps the wait for an engine
|
|
that never reports itself empty.
|
|
|
|
One character time is held after that and no more, because the count reaching 0
|
|
is late news rather than early. In a bridge log the count read 0 at 10670 ms and
|
|
the last character came back decoded at 11026 ms, and the decoder runs about
|
|
420 ms behind the air, so that character went out at about 10606 ms — before the
|
|
count read 0. What the character time covers is the reading, which happens every
|
|
`TypeAhead.PollInterval`, not the transmission. Turnaround time is worth more
|
|
than padding in a contest.
|
|
|
|
MMTTY has no stop character to send instead. Its macro language ends a
|
|
transmission with `\` at the end of a macro and stops the carrier with `~`, but
|
|
those are read by the macro interpreter, and the only way into the engine from
|
|
here is `PostMmttyMessage(4, ...)`, one typed character. Probe question 10 typed
|
|
both at the end of a message: both were swallowed and the transmitter stayed up.
|
|
|
|
Before this the stop was `AbortXmit` a fixed `StopPatience` after the polite
|
|
stop, which is a race. It was lost once in the log: on one of three CQs the
|
|
abort fired 4 ms before the count reached 0 and the carriage return at the end
|
|
of the message never went out.
|
|
|
|
Escape is the one that stops now: what has not gone to the engine is dropped and
|
|
the engine drops what it holds.
|
|
|
|
**What paces the feeder.** The clock, at one Baudot symbol every symbol time for
|
|
the baud rate in the digital settings, with the engine kept `Ahead` characters
|
|
ahead: one on the air and one in hand for when that one finishes. RTTY runs at a
|
|
fixed speed, so the clock is right.
|
|
|
|
A character is not a symbol. Most are one, but a digit sent while the engine is
|
|
in the letters shift costs a shift symbol and the digit, and the letter after it
|
|
costs a shift back: `OM5M` is six symbols, not four. Pacing one character per
|
|
symbol time ran ahead of the air by about one part in nine — 26 characters of
|
|
one CQ went out in 29 symbols — which showed up as the red text in the transmit
|
|
pane running ahead of the transmission, and as the engine's count building up
|
|
until the brake caught it. `TypeAhead.Symbols` prices a character against the
|
|
shift the engine is in, and both the feeder and the model of what is on the air
|
|
use it. They keep separate shift states, because the feeder is `Ahead`
|
|
characters in front of what is being transmitted.
|
|
|
|
**What a character costs cannot be calculated, so it is bounded.** The shift a
|
|
character needs depends on settings this program cannot see: unshift-on-space is
|
|
`TXUOS` in MMTTY's `UserPara.ini`, written per profile, and it is also a button
|
|
on MMTTY's own display that the operator can press during a contest. The FIG
|
|
button is another. `Symbols` assumes unshift-on-space is on, because MMTTY's
|
|
help says that is the usual setting and because assuming it charges a symbol too
|
|
many, which leaves the pane behind the air rather than in front of it.
|
|
|
|
The engine's count is what settles it, and it needs no setting to be read. The
|
|
count is in symbols and falls as they are transmitted, so what it drops between
|
|
two answers is what went on the air between them. That is the air's own rate,
|
|
measured rather than reckoned: `WentOutByCount` spends those symbols on the
|
|
characters at the front of what has not gone out and marks them as gone.
|
|
|
|
Two things make it self-correcting. The rate comes from the engine, so a clock
|
|
that runs a little fast or slow cannot drift. And a count of 0 means the engine
|
|
holds nothing, so everything it was given has gone out, whatever the symbols
|
|
added up to along the way — a wrong guess about the shift is squared off at the
|
|
end of every message rather than accumulating over a contest.
|
|
|
|
The one thing to be careful of is that the count trails what the engine was last
|
|
given by 100 to 150 ms, which the probe measured. A 0 newer than `CountLag` is
|
|
the answer not having caught up, not an empty engine, and is passed over. An
|
|
engine that does not count at all falls back to the clock.
|
|
|
|
Not the decoder. MMTTY hears its own transmission with sound loopback on and
|
|
reports it back, which would be the air itself, but that is the receive path: it
|
|
is off at many stations, and with a receiver actually listening another
|
|
station's `CQ` matches ours by chance. The transmit buffer count is the
|
|
transmitter's own report and has neither problem.
|
|
|
|
`MmttyEngine` has to declare `EngineBuffer` for any of the count to be used.
|
|
Without it the sender falls back to the plain path: whole `SendString` calls
|
|
instead of `PostMmttyMessage(4, ...)` one character at a time, and no count at
|
|
all. It ran that way for a while, and a bridge log of a CQ shows what it costs.
|
|
The feeder put 26 characters out at 165 ms each; MMTTY put them on the air at
|
|
183 ms each, because the digits in the callsign cost a shift to figures and the
|
|
letter after each one a shift back. By the end of the message the engine was
|
|
1.5 s behind, and `{RX}` was answered by the hard stop rather than by MMTTY
|
|
running dry. The brake is what holds that gap down.
|
|
|
|
The clock advances by exactly one symbol time per symbol handed over,
|
|
never from the time the poll happened. A poll is up to 50 ms late, and starting
|
|
the next character from there made every character a little late, the lateness
|
|
added up, and the engine ran dry and transmitted the idle tone between the
|
|
characters of a long word.
|
|
|
|
The engine's own count cannot be the pace, which the probe log shows twice over.
|
|
`TxBufLen` read 0 for the first 150 ms after twenty-one characters were pushed
|
|
and were already going out, and it read 0 for three seconds while the engine sat
|
|
holding `ABCD` on Word out. The count is only to be believed while it is large
|
|
and going down, and the feeder keeps the engine nearly empty on purpose — that is
|
|
what leaves the message editable — so it lives in the range where the count
|
|
reads 0. A feeder that took that 0 as room fed on it every poll and handed the
|
|
engine about three characters for every one on the air, which is what put the
|
|
red text ahead of the transmission.
|
|
|
|
So the count is a brake. `Slack` is how many symbols the engine may be behind
|
|
before the feeder waits for it: MMTTY answers in symbols, which is more than the
|
|
characters it was given, so an exchange full of digits is slower than the clock
|
|
thinks and this is what stops the clock running away on it. A count that is not
|
|
going down means the engine is holding what it has, and the brake lets go after
|
|
`HoldingPatience` so the space that releases a held word can get there.
|
|
|
|
What the engine probe established, with MMTTY 1.70 under Wine on a machine with
|
|
a sound card:
|
|
|
|
The answer being in symbols is why `Slack` is in symbols and the feeder keeps no
|
|
character count of its own to compare against it.
|
|
|
|
The report is worth reading for the receive side as well. With MMTTY's sound
|
|
loopback off, its help says the receive window is fed from the transmit window,
|
|
which would be a per-character report of what has gone out with no polling at
|
|
all. With loopback on, which is how this station runs it, the decoded text
|
|
arrives about 420 ms after the character goes out and matches what was sent.
|
|
|
|
**The protocol log.** Every line in and out of the bridge is written to
|
|
`Diagnostics/digital-<when>.log` under the settings folder, one file per run of
|
|
the engine, with a millisecond stamp on each. The buffer question is left out
|
|
and its answer written only when the count changes, so what is left is the
|
|
keying, the characters and the transmit reports. It is what says who dropped the
|
|
transmitter and when, which nothing else in the program can answer: the engine
|
|
probe cannot, because it runs the engine rather than the window.
|
|
|
|
```sh
|
|
./build.sh run --project tools/Nonemm.EngineProbe -- \
|
|
--engine ~/mmtty/MMTTY.EXE --prefix ~/.wine-nonemm --out engine-probe.log
|
|
```
|
|
|
|
It transmits on the sound card for about twenty seconds and keys no serial port
|
|
unless `--ptt` names one. `EngineProbe` states what each answer means.
|
|
|
|
**The transmitter dropping, and what ends a message.** `DigitalEngineSender`
|
|
holds three states — `Down`, `Keyed`, `Ending` — and its class comment carries
|
|
the transition table. The state is there rather than a flag because ending a
|
|
message takes as long as the engine takes to transmit what it holds, which is
|
|
seconds, and the operator can key again inside that time. With a flag, the
|
|
ending of one message went on running and put the key down in the middle of the
|
|
message after it. Each ending now belongs to one transmission and checks after
|
|
every step that it is still that transmission.
|
|
|
|
Coming back out of `Ending` costs something too. By then the stop is inside the
|
|
engine, waiting for its buffer to empty, and MMTTY fed while that stands stayed
|
|
keyed and transmitted nothing — the characters went in and never came out, and
|
|
the pane coloured them because `TxBufLen` answered 0, which is what an engine
|
|
that has transmitted everything also answers. So a macro pressed while the last
|
|
one is ending waits for what the engine holds to go out, clears the stop with
|
|
`AbortXmit`, and keys again. The wait is nothing at all in the case that shows
|
|
it: the last message has finished playing, which is why the operator pressed the
|
|
next key.
|
|
|
|
A message ends when `{RX}` or an abort has told the engine to stop and the
|
|
engine then reports the transmitter down. A report nobody asked for is
|
|
ignored. MMTTY reports the
|
|
transmitter down at other times — the buffer hands it one character at a time
|
|
and keeps it nearly empty — and taking that as the end cleared the pane and shut
|
|
the gate two or three characters into a message, leaving the rest of it to go
|
|
out on the next function key.
|
|
|
|
That report is not the only thing that starts the pane again, because it does
|
|
not always come. Two macros pressed one after the other keep the transmitter up
|
|
from the first to the last character, so the engine never reports a drop between
|
|
them, and the pane kept both messages. Everything the engine has been given is
|
|
locked — it cannot be taken back — so a run of macros left a pane with nothing
|
|
in it that could be edited. `DigitalEngineSender.StartAsync` now calls
|
|
`TypeAhead.Started` whenever it keys a new transmission, which drops the last
|
|
message off the pane and keeps whatever was typed ahead.
|
|
|
|
`TypeAhead.Ended` is called while the state lock is held, for the same reason.
|
|
The drop arrives from the engine on a thread of its own, and a macro pressed on
|
|
the last character of a message got as far as handing its own text over in the
|
|
gap between reading the state and clearing the pane: the new text was wiped off
|
|
the pane while the engine transmitted it. The order was found by reading the
|
|
code, not on the air, and the fix has not been tested against a real engine.
|
|
|
|
Which of the control's two paths carries the transmit state has not been
|
|
settled. The bridge takes it from `OnPttEvent`, and N1MM's `DigitalInterface`
|
|
binds an empty handler there and reads MMTTY's window message 32772 out of
|
|
`OnTranslateMessage` instead. Both are in the same class, so one of them is a
|
|
leftover, and `docs/digital-bridge.md` argues 32772 cannot reach
|
|
`OnTranslateMessage` at all while the control has an event for it. `OnPttEvent`
|
|
does report drops here, so nothing is missing; it is worth measuring only if the
|
|
reports turn out to be coarser than the message.
|
|
|
|
Keying is the keyer's, not the window's: `{TX}` and the TX button both go
|
|
through `DigitalEngineSender.Transmit`, so the order to key is always the first
|
|
thing the engine is told. MMTTY keys itself off a character given to it while
|
|
the transmitter is down and drops again when it has sent it, so a key that
|
|
arrived behind the text put a keyed-up gap in the middle of the message. A drop
|
|
in the middle of a message that is still going out also keys the transmitter
|
|
again, so what is left of the message does not go out into a transmitter that
|
|
is down.
|
|
|
|
The idle tone between what the operator types is MMTTY's own diddle, the Diddle
|
|
setting on its TX tab: LTR, the standard one, is what `mmtty/UserPara.ini`
|
|
carries here. Nothing is fed to produce it, so it needs the transmitter to stay
|
|
keyed. Whether MMTTY holds it there with an empty buffer has not been measured:
|
|
the probe run that says it does was on a machine with no sound card, where
|
|
MMTTY does not really transmit.
|
|
|
|
**The link between the logging computers.** N1MM has two networks and this
|
|
program now has both. Port 12060 carries XML to other programs — a spot tool, a
|
|
score poster — which is `StationNetwork`. Computer to computer, N1MM uses port
|
|
12070 and a different protocol: `StationLink`.
|
|
|
|
That protocol is a UDP broadcast to find the other computers and a TCP
|
|
connection to each of them, opened both ways. A message reads
|
|
|
|
```
|
|
DATA__07%SHACK-PC%QSO%2026-09-03 12:34:56%DL1ABC%…~__DATA
|
|
```
|
|
|
|
which is the sending station's number, its computer name, the message type and
|
|
the fields of that type. `%` and `~` cannot appear in a field, so N1MM writes
|
|
`!` in their place. What is read and sent: contacts, edits, deletes, resyncs,
|
|
`IAM`, the echo pair, chat, the pass frequency, transmit on and off, and where a
|
|
station is. The rest of N1MM's forty-odd types — the score and sked windows, the
|
|
log check, the spot lists, the serial-number pool — are passed over, which
|
|
N1MM's own reader also does for a type it does not know.
|
|
|
|
**The version has to match.** N1MM compares the version in the beacon with its
|
|
own and, when they differ, puts up "Software versions must match. Update N1MM+."
|
|
and drops the station. So the version this program broadcasts is a setting —
|
|
Config ▸ Edit Networked-Computer Names — and it has to be the version of the
|
|
N1MM copies beside it. Help ▸ About in N1MM says which. A station that
|
|
broadcasts something else is kept in the list and the network status window
|
|
names it, because that is the one fault where everything looks connected and
|
|
nothing arrives.
|
|
|
|
Where a station is goes out once a second when it has changed, rather than from
|
|
each of the dozen places it can change from. In a multi-single entry that is
|
|
what the other operators watch.
|
|
|
|
**The ten-minute rule.** `BandChangeRules` counts band changes and the stay on a
|
|
band, and the entry window shows the countdown. What was missing was the rule
|
|
itself: only a user-defined contest carried one, so every built-in contest
|
|
allowed anything. `BandChangeRules.ForCategory` now gives a multi-operator entry
|
|
with one or two transmitters a ten-minute stay, which is N1MM's fallback for
|
|
every contest that does not name a number of its own, and a contest that caps
|
|
the changes per hour overrides `Contest.BandChangesFor`. N1MM's per-contest table
|
|
is not repeated: it is a few hundred cases in a decompiled hash switch, and the
|
|
contests this station enters are the ones worth reading out of it one at a time.
|
|
|
|
What the link does not do yet: N1MM's resync, where a station that has been
|
|
away asks another for the contacts it missed, and the log check that compares
|
|
two stations' logs row by row. Both are message types this program can already
|
|
frame; neither is written. There is also no guard against two stations taking
|
|
the same station number.
|
|
|
|
**Voice keying.** `MessageSender` was written to cover a voice keyer playing a
|
|
recording, and nothing implements it. Each operator's recordings folder is
|
|
stored with the rest of his settings and waits on the same thing. No DVK support either, so the second radio
|
|
of an SO2R station cannot call CQ by voice. Alternating CQ therefore works on CW
|
|
only, though nothing in it is CW-specific: a voice keyer that reports when the
|
|
recording has finished would drive it as it stands.
|
|
|
|
**The QTC window sends on CW and RTTY, not on SSB.** On CW the header, the
|
|
lines, the QRV, the TU and the again messages go out through the keyer, with
|
|
N1MM's messages and N1MM's defaults.
|
|
|
|
On RTTY the station reading traffic out has Send All, which is N1MM's: the whole
|
|
series as one message, the heading, then every line with the operator's spacing
|
|
between them, then the ending, and `{TX}` and `{RX}` around the lot so it keys
|
|
the transmitter and drops it again. A line is the three fields joined with
|
|
hyphens, which is how N1MM writes them and it offers no setting for it. Snd n
|
|
sends one line again for a station that missed it. The heading, the ending and
|
|
the spacing are settings, N1MM's `WAESendAllHeadingText`,
|
|
`WAESendAllEndingText` and `WAESQTCSpacing`, with its defaults; `{ENTERLF}` in
|
|
them is new to the expander and stands for a carriage return and a line feed.
|
|
|
|
A whole series is the best part of a minute of transmission at 45 baud, which is
|
|
why it is one message rather than a button per line: the type-ahead buffer holds
|
|
it and paces the engine, and the operator presses one button. This has been
|
|
tested as text — what the message comes out as — and not on the air.
|
|
|
|
What is not here: N1MM's four SSB recordings — QRV, Agn, Cfm and TU — which need
|
|
a voice keyer, and its RTTY messages for the station taking traffic down, which
|
|
are `WAERXReadyText`, `WAEAllAgnText`, `WAEAGNText` and `WAESaveQTCText`. Those
|
|
four are typed into the digital window's transmit pane here.
|
|
|
|
**Cut numbers are not sent.** N1MM can key a serial number as letters — `N` for
|
|
9, `T` for 0 — and offers several styles. Nothing here does, in a QTC line or in
|
|
any other message.
|
|
|
|
**Two WAE numbers do not agree with N1MM, both from 2020 and 2021 logs.** The
|
|
disagreements are the country file of the day, not the rules: 4U1A counted as
|
|
`4U1V` then and as `OE` in today's `wl_cty.dat`, `KP2BH` counted as `KP2` then
|
|
and is listed as a US call now, and China was not split by call area yet. The
|
|
2022 to 2025 logs agree to the contact.
|
|
|
|
**Telnet window: what N1MM has and this does not.** Left out: the special-calls
|
|
list, and saving received spots to a database — N1MM keeps those spots in its
|
|
admin database, which is not the log file the two programs share, so there is
|
|
nothing to be compatible with and nothing asks to read them back. The band plan
|
|
tab is not repeated either, because Config ▸ Sub bands already edits the same
|
|
numbers.
|
|
|
|
The node list is downloaded from the public NG3K page rather than from N1MM's
|
|
own web service, which asks the operator to opt in to data collection and is
|
|
N1MM's to run. A page that changes shape would stop the download working; the
|
|
reading only needs `telnet://` links in a table, and the old list stays in place
|
|
when a download brings back something unreadable.
|
|
|
|
**Not every action macro is acted on.** The text macros and the action macros
|
|
that this program can carry out are listed in the README. What is read and
|
|
passed over: the `{CAT…}` and radio-hex families, the audio and rotator macros,
|
|
`{STEREOON}` and `{STEREOOFF}`, `{CONDJUMP}`,
|
|
`{QSYCQ}`, `{FORCELOG}`, `{SwapContests}`, the digital TNC macros (`{ENTER}`,
|
|
`{ESC}`, `{CTRL-A}`…), and the wav-directory macros, which wait on voice keying.
|
|
A message holding any of them still sends the right characters.
|
|
|
|
The CW keyer macros are not read either: `<` and `>` for speed, `~` for a half
|
|
space, and the prosign characters `]`, `[`, `+` and `=`. They belong to the
|
|
keyer rather than to the expander — cwdaemon and a WinKeyer each have their own
|
|
way of saying them — and they go out as the characters they are.
|
|
|
|
**The busted-spot check is one character wide.** N1MM offers two. Every call one
|
|
character away from the spotted one is looked up in the callsign database, which
|
|
is a few hundred lookups per spot; two characters away is a few hundred thousand,
|
|
which is too much work per spot for the little it would add.
|
|
|
|
**Two BARTG contests share one set of rules.** N1MM has a class each for the
|
|
March HF RTTY contest, `BARTGSRTTY`, and the September sprint, `BARTGRTTYS`.
|
|
This program has one contest, named `BARTGRTTYS`, holding the March rules — the
|
|
ones the station's log needs — and both of N1MM's names open a log with it. A
|
|
sprint log would be scored by the wrong rules, and a log started here writes
|
|
`BARTGRTTYS` into the contest name, which N1MM would read as the sprint.
|
|
|
|
**Digital interfaces other than MMTTY.** A contact can be logged as RTTY or
|
|
another digital mode and the contest rules score it, and the digital window
|
|
decodes and transmits through MMTTY over the Wine bridge. What is not here:
|
|
fldigi, MMVARI, 2Tone and the hardware TNCs, all of which N1MM drives.
|
|
`DigitalEngine` is the place to add them.
|
|
|
|
**Contest coverage.** Twenty-five families are built in, plus whatever `.udc` files
|
|
are in the user-defined folder. N1MM ships well over a hundred. Opening a log
|
|
from a contest that is in neither place fails with the contest name, which is
|
|
the right answer but it is still a wall.
|
|
|
|
Most of what is missing does not need code. A contest whose exchange is a
|
|
report and one value, scored by band, mode, continent or country, and counted
|
|
by country, zone, section or prefix, is a `.udc` file, and the file published
|
|
for N1MM is read as it is. Code is for the ones `.udc` has no words for: WAE
|
|
needs QTC traffic, IOTA needs island references as multipliers, and CQ WW RTTY
|
|
needed a third multiplier and its own points table because it shares a name
|
|
with the CW and SSB running of the contest.
|
|
|
|
`.udc` settings that are still passed over. `MultiplierBands` and
|
|
`QsoErrorString`, which are about the windows rather than the score.
|
|
`GenericPrintString`, which is the layout of a printed log rather than a
|
|
Cabrillo one.
|
|
|
|
`MultWindowType` is read as far as the score goes: it names the list a section
|
|
multiplier is checked against. Four of N1MM's lists are held here — the ARRL
|
|
sections, the US states, the Canadian provinces and the OK/OM districts — and a
|
|
file naming any of the hundred-odd others falls back to counting any exchange
|
|
that is not all digits.
|
|
|
|
`CabrilloString` and `CabrilloVersion` are read: a contest whose sponsor asks
|
|
for its own columns gets them, in N1MM's shape — the operator's callsign in the
|
|
first 13 columns, then every column padded to the width the file gives with one
|
|
space after it, and a value longer than its column cut rather than pushed. The
|
|
numbered `CabrilloFormat` layouts 2, 3, 4 and 5 — the NAQP, NA Sprint,
|
|
Sweepstakes and section-and-serial lines — are read as well, in N1MM's columns.
|
|
The exchange those layouts split into columns is built from the boxes the file
|
|
defines, in the order it defines them; N1MM builds it from the section box
|
|
alone, which writes only half of a two-box exchange. Layout 6, the ARRL RTTY
|
|
Roundup line, is not read, and a file asking for it gets the default line; no
|
|
published `.udc` file asks for it, because the Roundup has a contest class of
|
|
its own. `CabrilloFormat = 0` means the sponsor takes no Cabrillo log at all,
|
|
and nothing here stops the operator writing one.
|
|
|
|
N1MM's `.udc` format has no way to say that the exchange itself differs by the
|
|
other station's country. OK/OM DX is written in code for that reason — it asks
|
|
a county from OK and OM stations and a serial number from everyone else — and
|
|
`Contest.SkipsField` steps over the box that does not apply. REF, ARRL 10M, the
|
|
Ukrainian DX contest and the Russian DX RTTY contest ask their exchange the same
|
|
way, and are written in code for the same reason.
|
|
|
|
Every contest in the station's own logs can now be opened, and its 78 contest
|
|
instances score contact for contact as N1MM did apart from the cases below.
|
|
|
|
What still differs, and why:
|
|
|
|
- **The EU DX Contest** is written from N1MM's own class. The station's logs
|
|
were made with the `EU_DXC.udc` file instead, which scores a contact inside
|
|
your own country 1 point where the class scores 2, and counts a district code
|
|
once in the contest where the class counts it once per band.
|
|
- **Field Day Region 1**: every contact in that log holds 1 point and no
|
|
multiplier, which is not what N1MM's own class works out, so the log looks
|
|
like it was never scored. The rules here follow the class: 5 points for a
|
|
portable station, 2 for one in Region 1 at home and 3 from outside it.
|
|
- **SA 10 and OK/OM DX on SSB** are not written in code at all: both are
|
|
published as `.udc` files, and with those files in the user-defined contests
|
|
folder the station's logs score to the contact. N1MM's OK/OM DX class is the CW running only and says so; the SSB
|
|
running has different rules and its own pair of files, one for each side of
|
|
the contest.
|
|
- **Two OK/OM DX contacts** whose district code was stored with a trailing
|
|
space. N1MM checks the code against its list without trimming it, finds
|
|
nothing and counts no multiplier; the code is trimmed here and counts. Both
|
|
contacts are the same station in the 2026 log.
|
|
|
|
## Known rough edges
|
|
|
|
**The band plan covers 160M to 2M only.** 60M and everything above 2M get no
|
|
CW, digital or phone shading on the bandmap, because N1MM's own numbers for
|
|
those bands contradict themselves — its CW top for 1.25M is below its phone
|
|
start, and its 70CM CW top is above the band. Config ▸ Sub bands cannot add a
|
|
band that has no default.
|
|
|
|
**Available mults and Qs is only as good as the bandmap.** It lists what is
|
|
spotted, so a multiplier nobody has spotted is not there. N1MM's window also
|
|
shows the mults a contest has and nobody has worked, which needs a list of every
|
|
multiplier the contest counts. Only the section and state lists are held here,
|
|
so the zone and country lists would have to come from the country file first.
|
|
|
|
**Two entry windows and one keyer.** The SO2R box is told which radio to key
|
|
before each message, but nothing stops both radios sending at once if the
|
|
operator asks them to.
|