Commit Graph
15 Commits
Author SHA1 Message Date
erikbrockoandClaude Sonnet 5.5 1e0fb3eb08 Mark the CQ frequency on the bandmap and leave Run when the radio moves off it
Starting Run stores the frequency in the position. The bandmap draws a
purple, bold "CQ Frequency" pseudospot there, which is not added to the
shared bandmap, and a click on it tunes back and sets Run. A move of more
than 3 kHz (a setting on the telnet Filters tab) while running switches
to search and pounce.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 12:12:35 +00:00
erikbrockoandClaude Sonnet 5.5 3a39b40edb Fill the zone box from the log and the country file on space
Without a call history file, the CQ zone box stayed empty after the
callsign. It now takes the zone from the call history file, else from the
last logged contact with the call, else from the country file, as N1MM
does. A box that already holds text is left alone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 05:24:56 +00:00
erikbrockoandClaude Opus 5 b5043506ae Fill in the macros from N1MM's function key page
Going down N1MM's published list, the ones a contest operator would miss:

| | |
|---|---|
| `{LASTEXCH}` | what the last station sent, built from the boxes the contest asks for |
| `{ROVERQTH}` | where a rover is now |
| `{TIME}`, `{DATE}`, `{TIMEGMT}`, `{DATEGMT}` | the clock and the calendar |
| `{ESC}`, `{CTRL-A}` to `{CTRL-Z}` | the control characters a TNC reads as commands |
| `{CTRLHOME}` | the cursor back in the callsign box |
| `{F1}` to `{F12}` | another of this radio's keys, where `{CTRLF1}` is the other radio's |
| `{CQ}` | makes any key a CQ key: it starts running and carries alternating CQ on |
| `{DIGQTCS}`, `{DIGQTCR}` | opens the WAE QTC window the way asked for |
| `{CLEARRIT}` | the receive offset back to zero, which is rigctld's `J 0` |
| `{FORCELOG}` | logs what is in the boxes, holes and all |

`{FORCELOG}` is the one that gives something up. `LogContact` refuses an entry
that is short or holds a value the contest will not take, so that a caller who
has not checked cannot log nonsense; `LogContactAnyway` is the way past it,
because a contact in the log with a hole in it can be filled in afterwards and
one that was never logged cannot. It still needs a callsign, and the status
line says the row will need fixing before the log goes in.

A key that names itself would send for ever, so a key sending another key
ignores the macro a second time down.

What is still passed over is written down in the README and unfinished.md.
Advanced voicing is the one that matters: N1MM says a callsign or a serial by
playing a recording per letter out of `LettersFiles`, which is why `!` and `#`
say nothing on phone here and why F5 is empty in N1MM's own SSB file too. The
three packet BBS date formats are passed over because there is nothing here to
check a guess at them against.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
2026-09-11 08:24:34 +00:00
erikbrockoandClaude Opus 5 211298e04c Let the walk and the completeness check read one rule
A box the walk between the entry boxes steps over cannot be typed in, so it
must not hold the contact back either. `EntryFields.IsComplete` counted every
box marked required, while `MoveFocus` stepped over the ones the contest does
not ask this station for, so a contest with a box for some stations only
could lock the operator out of his own log.

The seven contests the scoring audit named do not do that any more —
`bae40bb` gave each of them the boxes N1MM shows, and fifteen reproductions
across all seven and both station types log cleanly — but the rule they broke
was still there to break again. Completeness now takes the same
applicability rule the cursor uses, and `OperatingPosition` answers it in one
place for the entry window, the ESM window and logging alike.

The audit is marked up for the three findings that are already fixed and for
item 14, which turns out to be what N1MM does rather than a fault.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
2026-09-10 20:33:30 +00:00
erikbrockoandClaude Opus 5 7ba0b44fc1 Take serial numbers from a pool the whole entry shares
Each station worked out its next number as the highest in its log plus one,
so two operators typing at the same moment both sent the same number and
one of the two contacts was wrong in the sponsor's log.

SerialNumberPool holds the highest number taken per band, which is N1MM's
QSONumbers array. A number is taken at the first character of the callsign,
as N1MM takes it, and ReserveNr tells the other stations before the contact
is worked. A number at or below one this station has used is answered with
the whole pool, N1MM's QSONRS, and the station that is behind counts on from
there. The pool also goes out when a contest is opened and when the numbers
are started again.

Contest.NumbersByBand says whether the numbers run per band or as one
sequence for the station. CQ WPX runs one per band for a multi-two and a
multi-multi; every other contest here runs one sequence. The band index in
the message is N1MM's, so the array lines up with theirs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
2026-09-10 11:56:49 +00:00
erikbrockoandClaude Opus 5 d5788da240 Wipe on Alt+W as well as put the boxes back
Alt+W only put the boxes back, so wiping needed Ctrl+W or the button.
It now goes both ways, the way N1MM's Wipe button does: with something in
the boxes it clears them, and pressed again on empty boxes it puts back
what it cleared.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
2026-09-10 11:30:07 +00:00
erikbrockoandClaude Opus 5 24542b8355 Put the wiped entry boxes back on Alt+W
N1MM wipes on Ctrl+W and puts the boxes back on Alt+W, which this had no
key for. A wipe now keeps the call, the exchange, the name, the comment
and the sent time, and Alt+W fills them in again.

Wiping empty boxes keeps what the wipe before it held, and the boxes are
only filled in again when they are empty, so nothing typed since the wipe
is thrown away. A call moved onto the call stack is not kept: it has not
gone anywhere and putting it back would make a second copy of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
2026-09-10 11:17:41 +00:00
erikbrockoandClaude Opus 5 ab9730b26d Check the entry boxes by the rules the log window uses
The entry window took any text and only the log window checked it, so the
same value was accepted in one place and refused in the other. The rules
now live in one file, ExchangeCheck, which both windows call.

OperatingPosition.EntryProblem names the first box that cannot be logged
and why. LogContact and SaveQuickEdit throw on one, so a caller that has
not checked cannot write nonsense to the log. The entry window checks
before it logs, puts the reason in the status line and the cursor in the
box to fix, which covers Enter, the Log It button, ESM and {LOG}.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
2026-09-10 11:13:38 +00:00
erikbrockoandClaude Opus 5 bae40bb04e Show the boxes N1MM shows for each contest
The entry window had a fixed set of boxes with a Name and Comment box beside
the exchange. N1MM lays out one row of boxes per contest and nothing else, so
this does the same: general logging keeps Name and Comment, and a contest gets
the boxes it exchanges.

Two new members on `Contest` carry the cases where the boxes and the log
columns are not the same list:

- `FromExchange1` spreads what was typed in one box across the log columns.
  Where the other station sends a serial or a district code depending on where
  it is, N1MM gives it one box and sorts the value into the number, section or
  zone column from there, so the score and the Cabrillo file read it from the
  same column N1MM does.
- `LogFieldsFor` gives the log window its own column list. Sweepstakes takes
  the whole exchange in one box and logs it in four columns; `SweepstakesExchange`
  parses `72 A 74 SCV` in any order, reading from the last word back as N1MM
  does.

The sent serial is now marked as not required and space steps over it along
with the two report boxes, because nobody types the number going out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
2026-09-09 20:12:37 +00:00
erikbrockoandClaude Opus 5 c8de74e24a Add the digital interface, running MMTTY under Wine
MMTTY and 2Tone have no socket or pipe interface: N1MM hosts XMMT.ocx and
exchanges window messages with the engine. A Linux process cannot load that
control, so bridge/nonemm-mmtty-bridge.exe hosts it under Wine and passes
lines over its standard input and output. docs/digital-bridge.md states the
protocol and what the control needs.

The window is N1MM's: receive pane with coloured callsigns, grab list, call
stacking, twenty-four macro buttons and the engine controls. One left click
copies what is under it — a callsign to the callsign box, anything else to the
exchange box the contest keeps for that kind of value.

Config > Digital registers XMMT.ocx in the Wine prefix on its own, making the
prefix first if it is not there. The engine, the bridge and the control start
at the copies shipped beside the program.

The bridge reads the control's own events. OnTranslateMessage carries only the
messages the control has no event for, so nothing was ever decoded through it.

hamlib's data mode names read as digital modes now, and a mode typed into the
callsign box changes mode the way a frequency changes band, so a station with
no radio can reach RTTY at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
2026-08-31 21:35:45 +00:00
erikbrockoandClaude Opus 5 12dab9644a Log the time the BARTG exchange went out
The BARTG RTTY contest sends the time with the serial number, and both
stations log the time the other one sent. The entry window now has the
report, serial and time boxes N1MM's has, and the time this station sent
is stamped into the MiscText column as the exchange message goes out, or
at log time when nothing sent it. The Cabrillo line writes that time and
falls back to the time of the contact when the column is empty, which is
what N1MM writes, in N1MM's columns.

The total score was points times every multiplier. N1MM's class works
out points times the countries and areas together, times the continents.
Per-contact points and multiplier flags do not change, so the station's
2022 log still scores contact for contact as N1MM did.

N1MM has a class each for the March HF RTTY contest and the September
sprint. This program has one contest holding the March rules under the
sprint's name, and docs/unfinished.md now says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
2026-08-31 13:29:08 +00:00
erikbrockoandClaude Opus 5 a07b181ee1 Write the numbered Cabrillo lines a .udc file asks for
CabrilloFormat 2, 3, 4 and 5 name the NAQP, NA Sprint, Sweepstakes and
section-and-serial lines. A file asking for one of those got the default
line; it now gets the layout, ported from CabrilloString2 and
CabrilloString4 in N1MM's Contact.cs, in N1MM's columns.

The exchange the 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 such as
NAQP's name and state.

Format 6, the ARRL RTTY Roundup line, is still not read. No published
.udc file asks for it, because the Roundup has a contest class of its
own.

ExchangeSlots.ValueOf moves to Nonemm.Contests as QsoExchange.ValueOf,
so the contest code can read a contact through its exchange boxes
without a second copy of that switch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
2026-08-31 12:27:49 +00:00
erikbrockoandClaude Opus 5 ec8cdb88d7 Stack the calls that answer a CQ, the way N1MM does
A CQ is answered by more than one station. N1MM lets the operator put the
others on a stack and work them one after another; this is that, with N1MM's
keys and macros.

Ctrl+Alt+G and {STACKANOTHER} put the call being typed on the stack and clear
the boxes. Alt+G brings the top call back. Alt+D drops it. {SOCALLSTACK} both
stacks and unstacks in one key: it swaps the typed call for the top one, stacks
it when the stack is empty, and takes the top call when the boxes are.
{LOGTHENPOP} and {LOGTHENNEXT} log the contact and bring the next caller in.
{CLRSTACK} empties it.

The rules are N1MM's, and CallStack and the entry actions hold them without a
window, so they are unit-tested: stacking only while running, nothing shorter
than two characters, no dupe, no frequency typed into the callsign box, a call
already on the stack moved rather than repeated, and a multiplier to the front.
{SOCALLSTACK} takes the stacked call off before it puts the typed one on, so a
multiplier just typed cannot come straight back.

Two places differ. N1MM refuses your own call only while the stack is empty;
it is never a station to work, so it is refused either way here. And the order
is multipliers first in every mode: N1MM's digital window offers first-in and
last-in as well, and there is no digital window here.

The stack window opens by itself with the first call and closes with the last,
or Window > Call Stack opens it. It does not take the keyboard when it opens,
because the operator is typing the next call. The next call out is ringed and
each is coloured by what working it would bring, as in every other window.

OperatingPosition.JudgeCall now answers what a call alone is worth, which the
stack window and the check window both need; it was private to the check
window before.

Driven under Xvfb: four calls stacked with Ctrl+Alt+G and listed in the window,
Alt+G bringing the top one into the boxes, Alt+D dropping the next.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
2026-08-30 22:23:30 +00:00
erikbrockoandClaude Opus 5 13a1acfcab Lay the band panel out the way N1MM lays it out, and colour it
N1MM's band panel is a row per band and a column per mode the contest runs,
with every cell painted by what the callsign being typed would be worth there:
red for one new multiplier, green for more than one, blue for points, grey for
a station already worked on that band and mode. With nothing typed every cell
is grey. That is the panel's point — it says where a station is still needed
without working the bands one at a time — and this had only the frame.

BandPanel holds the rule and is unit-tested without a window. The bands are the
six contest bands, with 30, 17 and 12 metres added for general logging: in N1MM
that is the one entry of the hundred and thirty-five that shows the WARC bands,
so Contest.ShowsWarcBands says so and GeneralLogging overrides it. The columns
are the modes the contest runs, so CQ WW CW has one column and IARU has two,
rather than always showing CW and phone. A digital entry gets one DIG column
where N1MM has RTTY and PSK: dupes and points here go by mode category, and
those two are the same category.

The labels are N1MM's: the metres alone up to 10, then the unit from 6m up. The
band the radio is on is ringed rather than filled, as N1MM rings it, so the
ring does not hide the colour underneath. The cells are labels with a click
handler rather than buttons, so the theme cannot paint over the colour under
the pointer.

Text on a filled cell is black or white by N1MM's rule: light is the three
channels adding up to more than 384.

Looked at under Xvfb in CQ WW CW: one CW column, six bands, grey with nothing
typed, and with a JA typed red on every band but 20, where the zone and country
are already claimed, and which carries the ring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
2026-08-30 21:42:45 +00:00
erikbrockoandClaude Opus 5 1c118f9ce4 Lay the menu bar out the way N1MM lays it out, and fill in the Edit menu
The menu tree comes from N1MM's EntryWindow.Designer.cs: File, Edit, View,
Tools, Config, Window, with the items under the names N1MM gives them. The
window list moved out of View into Window; the call history file moved from
Config into File > Import. Ctrl+W, Ctrl+L and Ctrl+M now work, so the keys
printed in the menu are real. Two commands that had no menu item before:
the CW and SSB function key definitions, and the QTC setup area.

The rest of the Edit menu:

- Ctrl+N asks for a note and puts it in the comment of the contact in the
  boxes, or of the last logged contact when nothing is typed.
- Edit Current Contact opens the Edit Contact form over what is typed but
  not logged. Update hands the values back to the entry boxes.
- Ctrl+Q and Ctrl+A load a logged contact into the boxes and paint them
  pale yellow. Enter writes the change back, Esc puts back what was being
  typed, and stepping forward past the newest contact leaves quick edit.
- Ctrl+U raises the received serial, or a numeric exchange box when the
  contest has no serial.
- Ctrl+F shows the call being typed in the log window, then the next
  contact with that call.

RadioPosition is now OperatingPosition: it is the operator at one radio,
not a place on a band.

Looked at under Xvfb on a 3775-contact log: quick edit back and forward,
Esc, the note dialog, find, and Edit Current Contact putting a zone back
into the entry boxes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 12:18:37 +00:00