Send contacts in the layout N1MM actually reads
QsoRecord followed MultiOpManager.QSOString, a helper N1MM does not use for the network. The messages use Contact.QSOString: 43 fields, the continent before the contact type, and flags as numbers. N1MM converts the multiplier flags with ToShort and threw on 'True', so every contact logged here raised an error at the other computer. The id now goes out as a GUID and is kept on arrival, which is what the other station's checksum of a resync block is made over. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,32 +3,38 @@ using Nonemm.Core;
|
||||
|
||||
namespace Nonemm.Network;
|
||||
|
||||
/// A contact as it travels between two logging computers: the thirty-five
|
||||
/// fields of N1MM's `QSOString`, in N1MM's order.
|
||||
/// A contact as it travels between two logging computers: the forty-three
|
||||
/// fields of N1MM's `Contact.QSOString`, in N1MM's order.
|
||||
///
|
||||
/// The order is what matters and it cannot be changed, because the other end
|
||||
/// reads the fields by position. It is taken from `MultiOpManager.cs:1007`.
|
||||
/// reads the fields by position, with `SetNewContact`. N1MM converts a flag
|
||||
/// with `ToShort`, so it has to be a number: it throws on `True`. The only
|
||||
/// flag it reads in words as well is `IsOriginal`.
|
||||
///
|
||||
/// | At | Field | At | Field |
|
||||
/// |---|---|---|---|
|
||||
/// | 0 | timestamp | 18 | sent serial |
|
||||
/// | 1 | callsign | 19 | points |
|
||||
/// | 2 | frequency, kHz | 20 | multiplier 1 |
|
||||
/// | 3 | transmit frequency | 21 | multiplier 2 |
|
||||
/// | 4 | mode | 22 | power |
|
||||
/// | 5 | contest name | 23 | band, MHz |
|
||||
/// | 6 | sent report | 24 | WPX prefix |
|
||||
/// | 7 | received report | 25 | exchange 1 |
|
||||
/// | 8 | country prefix | 26 | radio number |
|
||||
/// | 9 | station prefix | 27 | operator |
|
||||
/// | 10 | QTH | 28 | grid square |
|
||||
/// | 11 | name | 29 | contest number |
|
||||
/// | 12 | comment | 30 | multiplier 3 |
|
||||
/// | 13 | received serial | 31 | misc text |
|
||||
/// | 14 | section | 32 | contact type |
|
||||
/// | 15 | precedence | 33 | run 1 or run 2 |
|
||||
/// | 16 | check | 34 | continent |
|
||||
/// | 17 | zone | | |
|
||||
/// | 0 | timestamp | 22 | power |
|
||||
/// | 1 | callsign | 23 | band, MHz |
|
||||
/// | 2 | frequency, kHz | 24 | WPX prefix |
|
||||
/// | 3 | transmit frequency | 25 | exchange 1 |
|
||||
/// | 4 | mode | 26 | radio number |
|
||||
/// | 5 | contest name | 27 | operator |
|
||||
/// | 6 | sent report | 28 | grid square |
|
||||
/// | 7 | received report | 29 | contest number |
|
||||
/// | 8 | country prefix | 30 | multiplier 3 |
|
||||
/// | 9 | station prefix | 31 | misc text |
|
||||
/// | 10 | QTH | 32 | continent |
|
||||
/// | 11 | name | 33 | contact type |
|
||||
/// | 12 | comment | 34 | run 1 or run 2 |
|
||||
/// | 13 | received serial | 35 | rover location |
|
||||
/// | 14 | section | 36 | radio interfaced |
|
||||
/// | 15 | precedence | 37 | station number |
|
||||
/// | 16 | check | 38 | computer name |
|
||||
/// | 17 | zone | 39 | original |
|
||||
/// | 18 | sent serial | 40 | run QSO |
|
||||
/// | 19 | points | 41 | id |
|
||||
/// | 20 | multiplier 1 | 42 | claimed |
|
||||
/// | 21 | multiplier 2 | | |
|
||||
///
|
||||
/// The contact carries its points and its multiplier flags, and they are read
|
||||
/// rather than trusted: the log works them out again from the contest rules, so
|
||||
@@ -38,7 +44,7 @@ public static class QsoRecord
|
||||
{
|
||||
/// How many fields there are. A message with fewer is read as far as it
|
||||
/// goes, because a station on another version sends a shorter one.
|
||||
public const int FieldCount = 35;
|
||||
public const int FieldCount = 43;
|
||||
|
||||
public static List<string> Write(Qso qso) =>
|
||||
[
|
||||
@@ -63,8 +69,8 @@ public static class QsoRecord
|
||||
Text(qso.Zone),
|
||||
Text(qso.SentNumber),
|
||||
Text(qso.Points),
|
||||
StationRecord.Written(qso.IsMultiplier1),
|
||||
StationRecord.Written(qso.IsMultiplier2),
|
||||
Flag(qso.IsMultiplier1),
|
||||
Flag(qso.IsMultiplier2),
|
||||
qso.Power,
|
||||
StationRecord.Written(qso.Band?.MegahertzLabel ?? 0),
|
||||
qso.WpxPrefix,
|
||||
@@ -73,11 +79,20 @@ public static class QsoRecord
|
||||
qso.Operator,
|
||||
qso.GridSquare,
|
||||
Text(qso.ContestNumber),
|
||||
StationRecord.Written(qso.IsMultiplier3),
|
||||
Flag(qso.IsMultiplier3),
|
||||
qso.MiscText,
|
||||
qso.Continent,
|
||||
qso.ContactType,
|
||||
Text(qso.RunPosition),
|
||||
qso.Continent,
|
||||
qso.RoverLocation,
|
||||
Flag(qso.IsRadioInterfaced),
|
||||
Text(qso.NetworkedComputerNumber),
|
||||
qso.StationName,
|
||||
Flag(qso.IsOriginal),
|
||||
Flag(qso.IsRunQso),
|
||||
// N1MM parses this as a GUID and throws on anything else
|
||||
Guid.TryParse(qso.Id, out Guid id) ? id.ToString("N") : "",
|
||||
Flag(qso.IsClaimed),
|
||||
];
|
||||
|
||||
/// The contact the fields describe. `at` is where the fields start, which
|
||||
@@ -96,7 +111,7 @@ public static class QsoRecord
|
||||
Frequency transmit = Frequency.FromKilohertz(record.Decimal(at + 3));
|
||||
return new Qso
|
||||
{
|
||||
Id = Qso.NewId(),
|
||||
Id = Guid.TryParse(record.Field(at + 41), out Guid sent) ? sent.ToString("N") : Qso.NewId(),
|
||||
TimestampUtc = record.Time(at),
|
||||
Call = Callsign.Parse(record.Field(at + 1)),
|
||||
Frequency = frequency,
|
||||
@@ -128,15 +143,21 @@ public static class QsoRecord
|
||||
ContestNumber = record.Number(at + 29),
|
||||
IsMultiplier3 = record.Flag(at + 30),
|
||||
MiscText = record.Field(at + 31),
|
||||
ContactType = record.Field(at + 32),
|
||||
RunPosition = record.Number(at + 33),
|
||||
Continent = record.Field(at + 34),
|
||||
Continent = record.Field(at + 32),
|
||||
ContactType = record.Field(at + 33),
|
||||
RunPosition = record.Number(at + 34),
|
||||
RoverLocation = record.Field(at + 35),
|
||||
IsRunQso = record.Flag(at + 40),
|
||||
// a station on an older version stops before this
|
||||
IsClaimed = record.Field(at + 42).Length == 0 || record.Flag(at + 42),
|
||||
StationName = stationName,
|
||||
NetworkedComputerNumber = record.StationNumber,
|
||||
NetworkedComputerNumber = record.Number(at + 37) is > 0 and var made ? made : record.StationNumber,
|
||||
// it was made at another radio, on another computer
|
||||
IsOriginal = false,
|
||||
};
|
||||
}
|
||||
|
||||
private static string Flag(bool flag) => flag ? "1" : "0";
|
||||
|
||||
private static string Text(int number) => number.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user