Files
Nonemm/src/Nonemm.Contests/TransmitRules.cs
T
erikbrockoandClaude Opus 5 4eb8071d57 Say when the entry has two signals on the air
N1MM blocks the transmitter when a multi-operator entry is about to break
its own class. This program does not touch the transmitter: a logger that
decides for itself when the operator may not send loses a contact every
time it is wrong, and it is wrong whenever it has stale information about
the other stations — which, on a network, is whenever a laptop has just
been unplugged.

So the entry window says so instead. While this station is transmitting
and the entry is over what it is allowed, a red box goes across the top of
the window naming the station and the band. It is above the frequency and
under the menu, so nothing it covers is anything the operator needs while
he is sending, and it is big enough to read from the other side of the
room.

`TransmitRules` is what it asks:

| Entry | Allowed |
|---|---|
| one transmitter | one signal |
| two transmitters | two signals, on different bands |
| anything else | one signal per band |

Two on one band is wrong in every multi-operator class, so it is checked
whatever the category says. A station that has not said which band it is on
counts as somewhere else: a guess there puts the box in front of an
operator doing nothing wrong.

This is also the first thing that sends `XMIT`. The message was written and
nothing called it, so the transmit column of everybody's network status
window stayed empty and no station knew when another was sending. The entry
window now says when its key goes down and what it is sending.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PdAYHcdRktqKry7nk414TU
2026-09-03 23:23:46 +00:00

97 lines
3.9 KiB
C#

using Nonemm.Core;
namespace Nonemm.Contests;
/// One station of the entry with its key down, as the others see it.
public sealed record Transmitter(string ComputerName, Band? Band, ModeCategory? Mode);
/// How many signals a multi-operator entry may have on the air at once, and on
/// which bands.
///
/// N1MM blocks the transmitter when the entry is about to break these. This
/// program does not touch the transmitter — a logger that decides for itself
/// when the operator may not send is a logger that loses a contact when it is
/// wrong — so it says what is happening and leaves it to the operator.
///
/// | Entry | What is allowed |
/// |---|---|
/// | one transmitter | one signal on the air |
/// | two transmitters | two signals, on different bands |
/// | anything else | as many as the sponsor allows, one per band |
///
/// Two signals on one band is wrong in every multi-operator class, which is why
/// it is checked whatever the category says. An entry that is neither one nor
/// two transmitters — multi-unlimited, and a single operator with two radios —
/// is only held to that.
public static class TransmitRules
{
/// What is wrong with this station transmitting now, or null when nothing
/// is. `others` is every other station of the entry that has its key down.
public static string? Violation(
ContestEntry entry,
Transmitter me,
IReadOnlyList<Transmitter> others)
{
if (others.Count == 0)
{
return null;
}
if (others.FirstOrDefault(o => SameBand(o, me) && o.Mode == me.Mode) is { } sameMode)
{
return $"{sameMode.ComputerName} is transmitting on {Where(me)} as well";
}
if (others.FirstOrDefault(o => SameBand(o, me)) is { } sameBand)
{
return $"{sameBand.ComputerName} is transmitting on {me.Band?.Name} as well";
}
int allowed = Transmitters(entry);
if (allowed > 0 && others.Count + 1 > allowed)
{
string signals = $"{others.Count + 1} signals";
string on = string.Join(", ", others.Select(Where).Prepend(Where(me)));
return allowed == 1
? $"{signals} on the air — a one-transmitter entry may have one: {on}"
: $"{signals} on the air — a two-transmitter entry may have two: {on}";
}
return null;
}
/// How many signals the entry may have at once, or zero when the category
/// does not say. N1MM reads the number off the operator category on
/// Cabrillo 2.0 and off the transmitter category after it, and so does
/// this.
private static int Transmitters(ContestEntry entry)
{
if (!entry.OperatorCategory.StartsWith("MULTI", StringComparison.OrdinalIgnoreCase))
{
return 0;
}
if (Is(entry, "MULTI-ONE", "ONE"))
{
return 1;
}
return Is(entry, "MULTI-TWO", "TWO") ? 2 : 0;
}
private static bool Is(ContestEntry entry, string operatorCategory, string transmitterCategory) =>
entry.OperatorCategory.Equals(operatorCategory, StringComparison.OrdinalIgnoreCase)
|| entry.TransmitterCategory.Equals(transmitterCategory, StringComparison.OrdinalIgnoreCase);
/// Two stations are on the same band only when both say which band they are
/// on. A station that has not said counts as somewhere else: a guess here
/// puts a red box in front of an operator who is doing nothing wrong.
private static bool SameBand(Transmitter one, Transmitter other) =>
one.Band is not null && other.Band is not null && one.Band == other.Band;
private static string Where(Transmitter station) =>
$"{station.Band?.Name ?? "somewhere"} {Named(station.Mode)}".TrimEnd();
private static string Named(ModeCategory? mode) => mode switch
{
ModeCategory.Cw => "CW",
ModeCategory.Phone => "phone",
ModeCategory.Digital => "digital",
_ => "",
};
}