Keep the band when the frequency is past the edge
Contest operating goes outside the band edges. Someone answers a CQ at 14352, or a radio reports a frequency a few hundred hertz off, and Bands.ForFrequency returned null: the contact scored nothing, missed the per-band dupe check and exported with no band. N1MM buckets a frequency by its whole megahertz — its BandFor divides the kilohertz by 1000 and switches on the result, with 1 and 2 both 160M, 3 and 4 both 80M, 28 and 29 both 10M — and never looks at a band edge. Same rule here, except that the real edges are checked first: 2190M and 630M are both under 1 MHz and share the bucket, so nothing else can tell them apart. A frequency under 1 MHz that misses both edge ranges still has no band, as it does in N1MM. The bandmap clamped its slice to the band edges, so a radio past the edge left the receiver bar off the top of the scale. The slice now stretches to wherever the radio is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
13
README.md
13
README.md
@@ -219,7 +219,8 @@ to where it really is. That is what N1MM does, and the stacking follows its
|
||||
rule: place the label centred on its frequency, and push it below the one above
|
||||
if it would cover it.
|
||||
|
||||
The slice shown follows the receiver — 10, 20, 40 or 100 kHz, or the whole band.
|
||||
The slice shown follows the receiver — 10, 20, 40 or 100 kHz, or the whole band
|
||||
— and stretches past the band edge when the radio is past it.
|
||||
Callsigns are coloured by the same scorer as the entry window, so a dupe reads
|
||||
as a dupe here too. Clicking a callsign puts the radio there with the call
|
||||
already in the entry window; clicking anywhere else just moves the radio.
|
||||
@@ -231,6 +232,16 @@ other radio of a two-radio station.
|
||||
Band-plan colouring of the scale is not there. The segments differ by ITU region
|
||||
and the program has no band-plan table, so it would be guesswork.
|
||||
|
||||
### Band edges
|
||||
|
||||
A contact just outside a band edge keeps its band. `Bands.ForFrequency` first
|
||||
checks the real edges, and for anything outside them falls back to the whole
|
||||
megahertz: 14400 kHz is 20M, 7305 kHz is 40M. This is N1MM's rule, and it is
|
||||
there because contest operating goes past the edges — someone answers a CQ at
|
||||
14352, or a radio reports a frequency a few hundred hertz off — and a contact
|
||||
with no band scores nothing and exports wrong. Below 1 MHz the megahertz is
|
||||
always 0, so 2190M and 630M are told apart by their edges alone.
|
||||
|
||||
### The DX cluster
|
||||
|
||||
**Config → Cluster** takes the node's address, a password for the few nodes that
|
||||
|
||||
@@ -94,16 +94,20 @@ public sealed partial class BandmapWindow : RefreshableWindow
|
||||
return markers;
|
||||
}
|
||||
|
||||
/// The slice to show, centred on the receiver and kept inside the band.
|
||||
/// The slice to show, centred on the receiver. Contest operating goes
|
||||
/// outside the band edges, so the slice stretches to wherever the receiver
|
||||
/// is rather than leaving it off the top of the scale.
|
||||
private (Frequency Low, Frequency High) Window(Band band, Frequency centre)
|
||||
{
|
||||
long lowEdge = Math.Min(band.Low.Hertz, centre.Hertz);
|
||||
long highEdge = Math.Max(band.High.Hertz, centre.Hertz);
|
||||
Frequency span = Spans[Math.Max(0, SpanBox.SelectedIndex)].Span;
|
||||
if (span.Hertz == 0 || span.Hertz >= band.High.Hertz - band.Low.Hertz)
|
||||
if (span.Hertz == 0 || span.Hertz >= highEdge - lowEdge)
|
||||
{
|
||||
return (band.Low, band.High);
|
||||
return (Frequency.FromHertz(lowEdge), Frequency.FromHertz(highEdge));
|
||||
}
|
||||
long half = span.Hertz / 2;
|
||||
long low = Math.Clamp(centre.Hertz - half, band.Low.Hertz, band.High.Hertz - span.Hertz);
|
||||
long low = Math.Clamp(centre.Hertz - half, lowEdge, highEdge - span.Hertz);
|
||||
return (Frequency.FromHertz(low), Frequency.FromHertz(low + span.Hertz));
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,15 @@ public static class Bands
|
||||
private static Band Make(string name, double label, double lowKhz, double highKhz) =>
|
||||
new(name, label, Frequency.FromKilohertz(lowKhz), Frequency.FromKilohertz(highKhz));
|
||||
|
||||
/// Null when the frequency is outside every amateur allocation.
|
||||
/// The band a frequency counts as, or null when no band covers that
|
||||
/// megahertz.
|
||||
///
|
||||
/// Contest operating goes outside the band edges: an operator answering a
|
||||
/// CQ near the edge, or a radio reporting a frequency a few hundred hertz
|
||||
/// off. N1MM buckets a frequency by its whole megahertz rather than by the
|
||||
/// edges, so 14400 kHz is still 20M and the contact keeps its band. We do
|
||||
/// the same. Exact edges are checked first, because 2190M and 630M are both
|
||||
/// under 1 MHz and the bucket cannot tell them apart.
|
||||
public static Band? ForFrequency(Frequency f)
|
||||
{
|
||||
foreach (Band band in All)
|
||||
@@ -61,6 +69,18 @@ public static class Bands
|
||||
return band;
|
||||
}
|
||||
}
|
||||
long megahertz = f.Hertz / 1_000_000;
|
||||
if (megahertz == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
foreach (Band band in All)
|
||||
{
|
||||
if (megahertz >= band.Low.Hertz / 1_000_000 && megahertz <= band.High.Hertz / 1_000_000)
|
||||
{
|
||||
return band;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,30 @@ public class BandsTests
|
||||
public void FrequencyOutsideEveryAllocationHasNoBand() =>
|
||||
Assert.Null(Bands.ForFrequency(Frequency.FromKilohertz(12000)));
|
||||
|
||||
/// N1MM buckets by whole megahertz, so a contact made past the edge keeps
|
||||
/// its band instead of losing it.
|
||||
[Theory]
|
||||
[InlineData(14400, "20M")]
|
||||
[InlineData(7305, "40M")]
|
||||
[InlineData(1999.9, "160M")]
|
||||
[InlineData(4100, "80M")]
|
||||
[InlineData(29800, "10M")]
|
||||
[InlineData(21500, "15M")]
|
||||
public void FrequencyPastTheBandEdgeStillCountsAsThatBand(double kilohertz, string expected) =>
|
||||
Assert.Equal(expected, Bands.ForFrequency(Frequency.FromKilohertz(kilohertz))?.Name);
|
||||
|
||||
/// Below 1 MHz every band shares the same megahertz, so only the real edges
|
||||
/// decide.
|
||||
[Theory]
|
||||
[InlineData(137, "2190M")]
|
||||
[InlineData(475, "630M")]
|
||||
public void TheLowFrequencyBandsGoByTheirEdges(double kilohertz, string expected) =>
|
||||
Assert.Equal(expected, Bands.ForFrequency(Frequency.FromKilohertz(kilohertz))?.Name);
|
||||
|
||||
[Fact]
|
||||
public void BroadcastFrequenciesBelowOneMegahertzHaveNoBand() =>
|
||||
Assert.Null(Bands.ForFrequency(Frequency.FromKilohertz(900)));
|
||||
|
||||
[Fact]
|
||||
public void BandEdgesAreIncluded()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user