Add the core, contest, storage and format layers
Frequencies, bands, modes, callsigns, grid squares and the country file live in Nonemm.Core. Nonemm.Contests holds the scoring engine and CQ WW and CQ WPX. Nonemm.Storage writes N1MM's DXLOG schema, and Nonemm.Formats writes Cabrillo 3.0 and reads and writes ADIF. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Nonemm.Contests;
|
||||
using Nonemm.Core;
|
||||
|
||||
namespace Nonemm.Formats.Cabrillo;
|
||||
|
||||
/// Writes a Cabrillo 3.0 log: the header the sponsor's robot reads, then one
|
||||
/// line per contact.
|
||||
public sealed class CabrilloWriter
|
||||
{
|
||||
private readonly Contest contest;
|
||||
private readonly StationInfo me;
|
||||
|
||||
public CabrilloWriter(Contest contest, StationInfo me)
|
||||
{
|
||||
this.contest = contest;
|
||||
this.me = me;
|
||||
}
|
||||
|
||||
public string Write(CabrilloHeader header, IEnumerable<Qso> qsos)
|
||||
{
|
||||
StringBuilder text = new();
|
||||
text.Append("START-OF-LOG: 3.0\r\n");
|
||||
Line(text, "CONTEST", header.Contest);
|
||||
Line(text, "CALLSIGN", header.Callsign);
|
||||
Line(text, "CATEGORY-OPERATOR", header.OperatorCategory);
|
||||
Line(text, "CATEGORY-ASSISTED", header.AssistedCategory);
|
||||
Line(text, "CATEGORY-BAND", header.BandCategory);
|
||||
Line(text, "CATEGORY-MODE", header.ModeCategory);
|
||||
Line(text, "CATEGORY-POWER", header.PowerCategory);
|
||||
Line(text, "CATEGORY-STATION", header.StationCategory);
|
||||
Line(text, "CATEGORY-TRANSMITTER", header.TransmitterCategory);
|
||||
Line(text, "CATEGORY-OVERLAY", header.OverlayCategory);
|
||||
Line(text, "CATEGORY-TIME", header.TimeCategory);
|
||||
text.Append(CultureInfo.InvariantCulture, $"CLAIMED-SCORE: {header.ClaimedScore}\r\n");
|
||||
Line(text, "CLUB", header.Club);
|
||||
Line(text, "NAME", header.Name);
|
||||
foreach (string line in header.Address)
|
||||
{
|
||||
Line(text, "ADDRESS", line);
|
||||
}
|
||||
Line(text, "ADDRESS-CITY", header.AddressCity);
|
||||
Line(text, "ADDRESS-STATE-PROVINCE", header.AddressStateProvince);
|
||||
Line(text, "ADDRESS-POSTALCODE", header.AddressPostalcode);
|
||||
Line(text, "ADDRESS-COUNTRY", header.AddressCountry);
|
||||
Line(text, "EMAIL", header.Email);
|
||||
Line(text, "OPERATORS", header.Operators);
|
||||
foreach (string line in SplitLines(header.Soapbox))
|
||||
{
|
||||
Line(text, "SOAPBOX", line);
|
||||
}
|
||||
Line(text, "CREATED-BY", header.CreatedBy);
|
||||
|
||||
foreach (Qso qso in qsos.Where(q => q.IsClaimed).OrderBy(q => q.TimestampUtc))
|
||||
{
|
||||
text.Append(QsoLine(qso)).Append("\r\n");
|
||||
}
|
||||
text.Append("END-OF-LOG:\r\n");
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
public string QsoLine(Qso qso)
|
||||
{
|
||||
CabrilloExchange exchange = contest.CabrilloExchange(qso, me);
|
||||
StringBuilder line = new("QSO: ");
|
||||
line.Append(CabrilloBands.Designator(qso.Frequency).PadLeft(5)).Append(' ');
|
||||
line.Append(qso.Mode.CabrilloCode.PadRight(2)).Append(' ');
|
||||
line.Append(qso.TimestampUtc.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)).Append(' ');
|
||||
line.Append(qso.TimestampUtc.ToString("HHmm", CultureInfo.InvariantCulture)).Append(' ');
|
||||
line.Append(me.Callsign.PadRight(13)).Append(' ');
|
||||
Append(line, exchange.Sent);
|
||||
line.Append(qso.Call.Text.PadRight(13)).Append(' ');
|
||||
Append(line, exchange.Received);
|
||||
line.Append(qso.RadioNumber > 1 ? '1' : '0');
|
||||
return line.ToString();
|
||||
}
|
||||
|
||||
private static void Append(StringBuilder line, IReadOnlyList<CabrilloField> fields)
|
||||
{
|
||||
foreach (CabrilloField field in fields)
|
||||
{
|
||||
line.Append(field.Value.PadRight(field.Width)).Append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
private static void Line(StringBuilder text, string tag, string value)
|
||||
{
|
||||
if (value.Length > 0)
|
||||
{
|
||||
text.Append(tag).Append(": ").Append(value).Append("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> SplitLines(string text) =>
|
||||
text.Length == 0 ? [] : text.Split('\n').Select(l => l.TrimEnd('\r'));
|
||||
}
|
||||
Reference in New Issue
Block a user