Files
Nonemm/src/Nonemm.Contests/Rules/CqWpx.cs
T
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

107 lines
3.5 KiB
C#

using Nonemm.Core;
namespace Nonemm.Contests.Rules;
/// CQ WPX, CW, SSB and RTTY. Serial numbers are exchanged and the multiplier is
/// the prefix, counted once for the whole contest whatever the band.
public sealed class CqWpx : Contest
{
/// The rules split the bands at 14 MHz rather than by band name, so 30, 17
/// and 12 metres fall on the high side even though the contest is not run
/// on them.
private static readonly Frequency LowBandLimit = Frequency.FromKilohertz(14_000);
private readonly ModeCategory mode;
public CqWpx(ModeCategory mode) => this.mode = mode;
public string Name => mode switch
{
ModeCategory.Phone => "CQWPXSSB",
ModeCategory.Digital => "CQWPXRTTY",
_ => "CQWPXCW",
};
public string DisplayName => $"CQ WPX {ModeLabel()}";
public string CabrilloName => mode switch
{
ModeCategory.Cw => "CQ-WPX-CW",
ModeCategory.Phone => "CQ-WPX-SSB",
_ => "CQ-WPX-RTTY",
};
public IReadOnlyList<ExchangeField> ExchangeFieldsFor(StationInfo me) =>
[
new ExchangeField("Snt", ExchangeSlot.SentReport, ExchangeFieldKind.Report, IsRequired: false),
new ExchangeField("Rcv", ExchangeSlot.ReceivedReport, ExchangeFieldKind.Report),
new ExchangeField("SntNR", ExchangeSlot.SentNumber, ExchangeFieldKind.Number, IsRequired: false),
new ExchangeField("RcvNR", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number),
];
public IReadOnlyList<string> MultiplierNames => ["Prefixes"];
public DupeScope DupeScope => DupeScope.PerBand;
public bool HasSerialNumbers => true;
/// A multi-two and a multi-multi run a number sequence per band; every
/// other class runs one sequence for the whole station.
public bool NumbersByBand(ContestEntry entry) =>
EntryCategories.IsMultiTwo(entry) || EntryCategories.IsMultiMulti(entry);
public IReadOnlyList<ModeCategory> Modes => [mode];
/// The serial number is generated per contact, so the stored exchange is
/// the number the contest starts at.
public string SentExchangeFor(StationInfo me) => "001";
public int PointsFor(QsoContext qso)
{
bool highBand = qso.Qso.Frequency >= LowBandLimit;
bool rtty = mode == ModeCategory.Digital;
if (qso.IsSameCountry)
{
return rtty ? (highBand ? 1 : 2) : 1;
}
if (!qso.IsSameContinent)
{
return highBand ? 3 : 6;
}
if (rtty || qso.Continent == "NA")
{
return highBand ? 2 : 4;
}
return highBand ? 1 : 2;
}
public IReadOnlyList<Multiplier> MultipliersFor(QsoContext qso)
{
string? prefix = qso.Qso.Call.WpxPrefix();
return prefix is null ? [] : [new Multiplier(1, prefix, "")];
}
public int TotalScore(ScoreTally tally, ContestEntry entry) => tally.Points * tally.TotalMultipliers;
public CabrilloExchange CabrilloExchange(Qso qso, StationInfo me, ContestEntry entry) =>
new(
[
new CabrilloField(me.Callsign, 13),
new CabrilloField(qso.SentReport, 3),
new CabrilloField($"{qso.SentNumber:0000}", 6),
],
[
new CabrilloField(qso.Call.Text, 13),
new CabrilloField(qso.ReceivedReport, 3),
new CabrilloField($"{qso.ReceivedNumber:0000}", 6),
]);
private string ModeLabel() => mode switch
{
ModeCategory.Cw => "CW",
ModeCategory.Phone => "SSB",
_ => "RTTY",
};
}