From 0ad894bfae643850a8b947e32deb793b151b9350 Mon Sep 17 00:00:00 2001 From: ericek111 Date: Fri, 23 Oct 2026 14:18:42 +0000 Subject: [PATCH] Write LOCATION to Cabrillo, and give CQ WW M/2 eight band changes an hour CQ WW lists LOCATION as required, and the export never wrote it. It now carries the ARRL section, state or province, or DX. CQ WW M/2 has no stay on a band: it may make eight band changes in a clock hour. It was getting the ten-minute stay that M/S has. Co-Authored-By: Claude Sonnet 5.5 --- src/Nonemm.App/Windows/EntryWindow.Menu.cs | 15 +++++++++++++++ src/Nonemm.Contests/Rules/CqWorldWide.cs | 7 +++++++ src/Nonemm.Formats/Cabrillo/CabrilloHeader.cs | 4 ++++ src/Nonemm.Formats/Cabrillo/CabrilloWriter.cs | 1 + .../Nonemm.Contests.Tests/BandChangeRulesTests.cs | 15 +++++++++++++++ tests/Nonemm.Formats.Tests/CabrilloWriterTests.cs | 2 ++ 6 files changed, 44 insertions(+) diff --git a/src/Nonemm.App/Windows/EntryWindow.Menu.cs b/src/Nonemm.App/Windows/EntryWindow.Menu.cs index 059f12d..555356a 100644 --- a/src/Nonemm.App/Windows/EntryWindow.Menu.cs +++ b/src/Nonemm.App/Windows/EntryWindow.Menu.cs @@ -122,6 +122,7 @@ public sealed partial class EntryWindow TransmitterCategory = Logging.Instance.TransmitterCategory, OverlayCategory = Logging.Instance.OverlayCategory, TimeCategory = Logging.Instance.TimeCategory, + Location = CabrilloLocation(Logging.Me), ClaimedScore = Logging.Log.TotalScore, Club = Logging.Me.Club, Name = Logging.Me.Name, @@ -132,6 +133,20 @@ public sealed partial class EntryWindow Status($"{Logging.Log.Qsos.Count} contacts written to {Path.GetFileName(path)}"); } + /// The section, state or province for a station in the US or Canada, and + /// `DX` for any other station. + private static string CabrilloLocation(StationInfo me) + { + foreach (string place in new[] { me.ArrlSection, me.State, me.Province }) + { + if (place.Trim().Length > 0) + { + return place.Trim().ToUpperInvariant(); + } + } + return "DX"; + } + /// The contacts alone, which is the file to hand to another program. ADIF /// has no QTC record, and N1MM leaves the traffic out for that reason. private void OnExportAdif(object? sender, RoutedEventArgs e) => diff --git a/src/Nonemm.Contests/Rules/CqWorldWide.cs b/src/Nonemm.Contests/Rules/CqWorldWide.cs index ebf5e62..528fa30 100644 --- a/src/Nonemm.Contests/Rules/CqWorldWide.cs +++ b/src/Nonemm.Contests/Rules/CqWorldWide.cs @@ -67,6 +67,13 @@ public sealed class CqWorldWide : Contest public bool HasSerialNumbers => false; + /// Multi-single stays ten minutes on a band, which is the category default. + /// Multi-two has no stay: it may make eight band changes in a clock hour. + public BandChangeRules BandChangesFor(ContestEntry entry) => + EntryCategories.IsMultiTwo(entry) + ? new BandChangeRules(8) + : BandChangeRules.ForCategory(entry); + public IReadOnlyList Modes => [mode]; /// The report is fixed for the whole contest, so the sent exchange is the diff --git a/src/Nonemm.Formats/Cabrillo/CabrilloHeader.cs b/src/Nonemm.Formats/Cabrillo/CabrilloHeader.cs index 326ed70..d29fef6 100644 --- a/src/Nonemm.Formats/Cabrillo/CabrilloHeader.cs +++ b/src/Nonemm.Formats/Cabrillo/CabrilloHeader.cs @@ -27,6 +27,10 @@ public sealed record CabrilloHeader public string TimeCategory { get; init; } = ""; + /// The state, province or ARRL section of a station in the US or Canada, + /// and `DX` for everywhere else, which is what CQ WW asks for. + public string Location { get; init; } = ""; + public long ClaimedScore { get; init; } public string Club { get; init; } = ""; diff --git a/src/Nonemm.Formats/Cabrillo/CabrilloWriter.cs b/src/Nonemm.Formats/Cabrillo/CabrilloWriter.cs index 0f81d88..222d7f2 100644 --- a/src/Nonemm.Formats/Cabrillo/CabrilloWriter.cs +++ b/src/Nonemm.Formats/Cabrillo/CabrilloWriter.cs @@ -35,6 +35,7 @@ public sealed class CabrilloWriter Line(text, "CATEGORY-TRANSMITTER", header.TransmitterCategory); Line(text, "CATEGORY-OVERLAY", header.OverlayCategory); Line(text, "CATEGORY-TIME", header.TimeCategory); + Line(text, "LOCATION", header.Location); text.Append(CultureInfo.InvariantCulture, $"CLAIMED-SCORE: {header.ClaimedScore}\r\n"); Line(text, "CLUB", header.Club); Line(text, "NAME", header.Name); diff --git a/tests/Nonemm.Contests.Tests/BandChangeRulesTests.cs b/tests/Nonemm.Contests.Tests/BandChangeRulesTests.cs index b5c9858..9f7e46c 100644 --- a/tests/Nonemm.Contests.Tests/BandChangeRulesTests.cs +++ b/tests/Nonemm.Contests.Tests/BandChangeRulesTests.cs @@ -56,6 +56,21 @@ public class BandChangeRulesTests Assert.Equal(TimeSpan.FromMinutes(10), rules.MinimumStay); } + [Fact] + public void CqWwMultiTwoMayMakeEightBandChangesAnHourAndHasNoStay() + { + Contest contest = new Rules.CqWorldWide(ModeCategory.Phone); + + BandChangeRules rules = contest.BandChangesFor(new ContestEntry + { + OperatorCategory = "MULTI-OP", + TransmitterCategory = "TWO", + }); + + Assert.Equal(8, rules.Max); + Assert.False(rules.HasStayTimer); + } + [Fact] public void ASingleOperatorEntryHasNoBandChangeRule() { diff --git a/tests/Nonemm.Formats.Tests/CabrilloWriterTests.cs b/tests/Nonemm.Formats.Tests/CabrilloWriterTests.cs index 66bada3..27cfb6a 100644 --- a/tests/Nonemm.Formats.Tests/CabrilloWriterTests.cs +++ b/tests/Nonemm.Formats.Tests/CabrilloWriterTests.cs @@ -70,6 +70,8 @@ public class CabrilloWriterTests Assert.StartsWith("START-OF-LOG: 3.0\r\n", log); Assert.Contains("CONTEST: CQ-WW-CW\r\n", log); Assert.Contains("CLAIMED-SCORE: 1234\r\n", log); + Assert.Contains("LOCATION: DX\r\n", writer.Write( + new CabrilloHeader { Contest = "CQ-WW-CW", Callsign = "DL1ABC", Location = "DX" }, [])); Assert.EndsWith("END-OF-LOG:\r\n", log); }