Get WAE ready: the score split, the Cabrillo layouts and the QTC list
The score summary now splits the way N1MM's does. `ContestLog` keeps a tally per band, and per kind of row where the contest has kinds, and the score window reads those instead of adding the rows up itself. Three things were wrong with the old sum: - The band rows counted a multiplier as one whatever the band. WAE weights it by band — `ScoreRow.AddContact` adds four on 3.5, three on 7 and two on 14, 21 and 28 — so the band rows did not add up to the total. - The band rows counted contacts left out of the claimed score. - There was no split by kind. `CWAE.LogInfo` and `ScoreClass.Description` give WAE a `Q/QTC` column and a row per band and kind, so contacts, traffic sent and traffic received are read apart. The Cabrillo writer now pads the record name to seven characters, which is what `ConvertCabrilloStructStdToString` does and why: `X-QSO:` is six characters and a space, so every record name puts the frequency in the same column. Nonemm wrote five, and the `X-QSO:` lines added last commit did not line up with the rest. WAE's own layouts follow `CWAE.CabrilloString`: three spaces after the sent serial rather than the one every other column gets, and the transmitter column only for a multi-operator entry. `QtcTraffic.ToSend` no longer offers a contact marked as a dupe, which is what `DBSupport.QTCRecordsetCount` leaves out with `Trim(ContactType) = ''`. Checked by running the program against a WAE SSB log of six contacts and a series of ten received: the band rows read 4, 3, 2 and 2 and add up to the 11 in the total row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
This commit is contained in:
@@ -106,6 +106,12 @@ public interface Contest
|
||||
/// The log window then shows the series they belong to.
|
||||
bool HasQtcTraffic => false;
|
||||
|
||||
/// Which kind of row this is, for a contest whose score is broken out by
|
||||
/// kind. WAE separates contacts from the traffic sent and received, and
|
||||
/// N1MM's score window gives each kind a row of its own on every band.
|
||||
/// Every other contest has one kind and the column is not shown.
|
||||
string RowKindFor(Qso qso) => "";
|
||||
|
||||
string SentExchangeFor(StationInfo me);
|
||||
|
||||
int PointsFor(QsoContext qso);
|
||||
|
||||
@@ -14,6 +14,7 @@ public sealed class ContestLog
|
||||
private readonly List<Qso> qsos = [];
|
||||
private readonly Dictionary<string, DateTime> workedKeys = new(StringComparer.Ordinal);
|
||||
private readonly HashSet<string> claimedMultipliers = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<ScoreKey, ScoreTally> rows = [];
|
||||
private ScoreTally tally = new();
|
||||
|
||||
public ContestLog(
|
||||
@@ -42,6 +43,11 @@ public sealed class ContestLog
|
||||
|
||||
public ScoreTally Tally => tally;
|
||||
|
||||
/// The same score split the way N1MM's score window splits it: a row per
|
||||
/// band, and per kind of row as well where the contest has kinds. The
|
||||
/// rows add up to `Tally`.
|
||||
public IReadOnlyDictionary<ScoreKey, ScoreTally> Rows => rows;
|
||||
|
||||
public int TotalScore => contest.TotalScore(tally, Entry);
|
||||
|
||||
/// What logging this contact right now would do, without logging it.
|
||||
@@ -181,9 +187,11 @@ public sealed class ContestLog
|
||||
return;
|
||||
}
|
||||
bool counts = qso.IsInScore;
|
||||
ScoreTally row = RowFor(qso);
|
||||
if (counts)
|
||||
{
|
||||
tally.AddQso(verdict.Points);
|
||||
row.AddQso(verdict.Points);
|
||||
}
|
||||
if (!contest.IsContact(qso))
|
||||
{
|
||||
@@ -196,6 +204,7 @@ public sealed class ContestLog
|
||||
if (counts)
|
||||
{
|
||||
tally.AddMultiplier(multiplier);
|
||||
row.AddMultiplier(multiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,10 +217,22 @@ public sealed class ContestLog
|
||||
|
||||
/// Rescores the whole log. A removed or edited contact can hand its
|
||||
/// multiplier to a later one, which only a full pass gets right.
|
||||
private ScoreTally RowFor(Qso qso)
|
||||
{
|
||||
ScoreKey key = new(qso.Band?.Name ?? "", contest.RowKindFor(qso));
|
||||
if (!rows.TryGetValue(key, out ScoreTally? row))
|
||||
{
|
||||
row = new ScoreTally();
|
||||
rows[key] = row;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
workedKeys.Clear();
|
||||
claimedMultipliers.Clear();
|
||||
rows.Clear();
|
||||
tally = new ScoreTally();
|
||||
List<Qso> ordered = qsos.OrderBy(q => q.TimestampUtc).ToList();
|
||||
qsos.Clear();
|
||||
|
||||
@@ -65,6 +65,12 @@ public sealed class Wae : Contest
|
||||
|
||||
public bool HasQtcTraffic => true;
|
||||
|
||||
/// N1MM's score window gives contacts, traffic sent and traffic received a
|
||||
/// row each on every band, under the heading `Q/QTC`. The value is what the
|
||||
/// `Exchange1` column holds.
|
||||
public string RowKindFor(Qso qso) =>
|
||||
WaeQtc.From(qso) is { } qtc ? (qtc.IsSent ? WaeQtc.Sent : WaeQtc.Received) : WaeQtc.Contact;
|
||||
|
||||
public string SentExchangeFor(StationInfo me) => "001";
|
||||
|
||||
/// A QTC line counts, and so does every contact on a contest band. The
|
||||
@@ -106,13 +112,20 @@ public sealed class Wae : Contest
|
||||
[
|
||||
new CabrilloField(me.Callsign, 13),
|
||||
new CabrilloField(qso.SentReport, 3),
|
||||
new CabrilloField($"{qso.SentNumber:0000}", 4),
|
||||
// N1MM leaves three spaces after the sent serial rather
|
||||
// than the one every other column gets
|
||||
new CabrilloField($"{qso.SentNumber:0000}", 6),
|
||||
],
|
||||
[
|
||||
new CabrilloField(qso.Call.Text, 13),
|
||||
new CabrilloField(qso.ReceivedReport, 3),
|
||||
new CabrilloField($"{qso.ReceivedNumber:0000}", 4),
|
||||
]);
|
||||
])
|
||||
{
|
||||
// N1MM writes the transmitter column for a multi-operator
|
||||
// entry and nothing at all for a single operator
|
||||
AddsTransmitter = entry.OperatorCategory == "MULTI-OP",
|
||||
};
|
||||
}
|
||||
string receiver = qtc.IsSent ? qtc.Station.Text : me.Callsign;
|
||||
string sender = qtc.IsSent ? me.Callsign : qtc.Station.Text;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Nonemm.Contests;
|
||||
|
||||
/// Which row of the score window a contact belongs to. `Kind` is empty for
|
||||
/// every contest but WAE, which gives contacts, traffic sent and traffic
|
||||
/// received a row each on every band.
|
||||
public readonly record struct ScoreKey(string Band, string Kind);
|
||||
Reference in New Issue
Block a user