Move and batch spots the way N1MM does
Two differences from N1MM in the spot path, both found by reading Packet.cs. Randomising moved every CW spot. PacketSpot.Randomize moves a spot only when it is not a dupe, not a self spot, not on a CQ frequency, is on CW and is not working split, and Packet skips it altogether for a station passed to another band. Moving a dupe or a split station makes it harder to find, not easier, so this now leaves the same ones alone. Whether a station has been worked comes from the log, so AppSession asks it before moving a spot. Split needed knowing where a station is listening, which is in the comment. Qsx reads it by N1MM's rules: QSX, UP, DOWN, DN, U or D, the word standing alone or after a space, a bare number after QSX meaning kilohertz inside the band and anything else an offset from the spot, an offset over a hundred kilohertz refused unless it says QSX, DN70 read as a grid square rather than five down, and a listening frequency outside the band thrown away. Spots reached the bandmap one at a time, each one redrawing every window that watches it. N1MM queues them and flushes the queue once a second, which is what this does now: the filters and the randomiser run over the batch, and the bandmap takes it in one call and raises one change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,13 @@ public sealed class AppSession : IDisposable
|
||||
|
||||
private readonly Random jitter = new();
|
||||
|
||||
/// Spots wait here until the next flush. N1MM collects them the same way,
|
||||
/// because a skimmer feed sends more spots a minute than any window can
|
||||
/// usefully redraw.
|
||||
private readonly List<Spot> arriving = [];
|
||||
private readonly Lock arrivingGuard = new();
|
||||
private readonly Timer spotFlush;
|
||||
|
||||
public AppSession(UserPaths paths, Settings settings)
|
||||
{
|
||||
Paths = paths;
|
||||
@@ -45,8 +52,13 @@ public sealed class AppSession : IDisposable
|
||||
UserDefinedContestProblems = problems;
|
||||
BandPlan = settings.ToBandPlan();
|
||||
ApplySpotSettings();
|
||||
spotFlush = new Timer(_ => FlushSpots(), null, SpotFlushInterval, SpotFlushInterval);
|
||||
}
|
||||
|
||||
/// How often the spots that have arrived go onto the bandmap. N1MM's own
|
||||
/// queue is flushed once a second.
|
||||
private static readonly TimeSpan SpotFlushInterval = TimeSpan.FromSeconds(1);
|
||||
|
||||
public UserPaths Paths { get; }
|
||||
|
||||
public Settings Settings { get; private set; }
|
||||
@@ -466,17 +478,59 @@ public sealed class AppSession : IDisposable
|
||||
Changed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// A spot the node sent, kept or dropped by the filters on the telnet
|
||||
/// window.
|
||||
/// A spot the node sent. It waits for the next flush rather than going
|
||||
/// straight onto the bandmap.
|
||||
private void TakeSpot(Spot spot)
|
||||
{
|
||||
if (!spotFilter.Accepts(spot))
|
||||
lock (arrivingGuard)
|
||||
{
|
||||
return;
|
||||
arriving.Add(spot);
|
||||
}
|
||||
Bandmap.Add(Settings.RandomizeSpots ? SpotJitter.Shifted(spot, BandPlan, jitter) : spot);
|
||||
}
|
||||
|
||||
/// The spots that arrived since the last flush, filtered and moved about,
|
||||
/// put on the bandmap in one go.
|
||||
private void FlushSpots()
|
||||
{
|
||||
List<Spot> batch;
|
||||
lock (arrivingGuard)
|
||||
{
|
||||
if (arriving.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
batch = [.. arriving];
|
||||
arriving.Clear();
|
||||
}
|
||||
List<Spot> keeping = [];
|
||||
foreach (Spot spot in batch)
|
||||
{
|
||||
if (spotFilter.Accepts(spot))
|
||||
{
|
||||
keeping.Add(Settings.RandomizeSpots
|
||||
? SpotJitter.Shifted(spot, BandPlan, jitter, IsDupe(spot))
|
||||
: spot);
|
||||
}
|
||||
}
|
||||
if (keeping.Count > 0)
|
||||
{
|
||||
Bandmap.AddRange(keeping);
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the station has already been worked, which decides whether a
|
||||
/// spot is moved about: N1MM leaves a dupe where it is.
|
||||
private bool IsDupe(Spot spot) =>
|
||||
Logging is not null && Logging.Log.Judge(new Qso
|
||||
{
|
||||
Id = "",
|
||||
TimestampUtc = spot.AtUtc,
|
||||
Call = spot.Call,
|
||||
Frequency = spot.Frequency,
|
||||
Mode = Position?.Mode ?? Modes.Cw,
|
||||
ContestName = Logging.Contest.Name,
|
||||
}) is { IsDupe: true };
|
||||
|
||||
private void ApplySpotSettings()
|
||||
{
|
||||
spotFilter = Settings.SpotFilter.ToFilter(BandPlan, Settings.Station, Countries, Calls, History);
|
||||
@@ -499,6 +553,7 @@ public sealed class AppSession : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
spotFlush.Dispose();
|
||||
DisposeRadios();
|
||||
cluster?.Dispose();
|
||||
network?.Dispose();
|
||||
|
||||
Reference in New Issue
Block a user