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 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 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 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 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", }; }