Measure a QSY spot from where the call was first seen

The radio reports the frequency in small steps as the knob turns, and
each step was compared with the one before, so no step passed the
threshold. The move is now measured from the frequency the radio had
when the call appeared in the entry window. The threshold is a box on
the Filters tab of the telnet window.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 11:51:05 +00:00
co-authored by Claude Sonnet 5.5
parent 45040dff83
commit 70c16a563f
3 changed files with 28 additions and 1 deletions
+24 -1
View File
@@ -28,10 +28,32 @@ public sealed partial class AppSession
Dispatcher.UIThread.Post(() => SpotOnQsy(position, before));
}
private void SpotOnQsy(OperatingPosition position, Frequency before)
/// Where the radio was when the call in each entry window was first seen.
/// A radio reports every few hundred hertz as the knob turns, so the move
/// is measured from here and not from the previous report.
private readonly Dictionary<OperatingPosition, (string Call, Frequency At)> heardAt = [];
private void SpotOnQsy(OperatingPosition position, Frequency reported)
{
string call = position.Entry.Call.Trim().ToUpperInvariant();
Frequency now = position.Frequency;
if (call.Length == 0)
{
heardAt.Remove(position);
return;
}
if (!heardAt.TryGetValue(position, out var anchor) || anchor.Call != call)
{
anchor = (call, reported);
heardAt[position] = anchor;
}
if (Bands.ForFrequency(anchor.At) != Bands.ForFrequency(now))
{
// a band change starts again from the new band
heardAt[position] = (call, now);
return;
}
Frequency before = anchor.At;
Band? band = Bands.ForFrequency(before);
if (call.Length < ShortestCallToSpot
|| call.All(char.IsAsciiDigit)
@@ -58,6 +80,7 @@ public sealed partial class AppSession
SpotSource.Operator,
position.Me.Callsign,
$"Local spot by {Settings.CurrentOperator}");
heardAt.Remove(position);
Bandmap.Add(spot);
SendSpots([spot]);
_ = link?.SendSpotAsync(new SharedSpot(
@@ -49,6 +49,7 @@ public sealed partial class TelnetWindow
BlockedSpottersBox.Text = string.Join(' ', filter.BlockedSpotters);
BlockedCallsBox.Text = string.Join(' ', filter.BlockedCalls);
TimeoutBox.Text = session.Settings.SpotTimeoutMinutes.ToString(CultureInfo.InvariantCulture);
QsySpotBox.Text = session.Settings.QsySpotHertz.ToString(CultureInfo.InvariantCulture);
SpotCommentBox.Text = session.Settings.SpotComment;
}
@@ -72,6 +73,7 @@ public sealed partial class TelnetWindow
},
RandomizeSpots = RandomizeBox.IsChecked == true,
SpotTimeoutMinutes = Minutes(TimeoutBox.Text, session.Settings.SpotTimeoutMinutes),
QsySpotHertz = Minutes(QsySpotBox.Text, session.Settings.QsySpotHertz),
});
Show("*** filters applied — they hold for the spots that arrive from now on", NoticeColour);
}
@@ -122,6 +122,8 @@
<TextBox Name="BlockedCallsBox" />
<TextBlock Classes="label" Text="Bandmap spot timeout (minutes)" />
<TextBox Name="TimeoutBox" Width="60" HorizontalAlignment="Left" />
<TextBlock Classes="label" Text="Spot a call locally when the radio moves more than (Hz)" />
<TextBox Name="QsySpotBox" Width="60" HorizontalAlignment="Left" />
<Button Content="Apply" Click="OnApplyFilters" Margin="0,10,0,0" />
</StackPanel>
</ScrollViewer>