A spot at 10131.5 showed as DATA in the cluster list and raised an alert announcing SSB. Two tables answered the same question: the frontend knew 10.130-10.150 is the 30 m data segment, while the alert engine knew a CW range that stopped at 10.130 and ended with a bare 'return SSB' for everything else. That fall-through was the fault. A band plan that answers 'SSB' for anything it does not recognise is not a band plan, it is a default wearing one — and it fed the alert text, the FlexRadio spot colours and the mode the radio is told to switch to. The Go table is now transcribed from the frontend's, segment for segment, and returns nothing where it knows nothing. The order matters and is now load-bearing — the FT8 and FT4 watering holes are listed BEFORE the wide data blocks they sit inside — so there is a test that fails if anybody sorts the table by frequency and quietly turns every FT8 hole into DATA. WPM counts as CW while we are here: nothing else is reported in words per minute, and RBN puts it on every line.
72 lines
2.5 KiB
Go
72 lines
2.5 KiB
Go
package alerts
|
|
|
|
import "testing"
|
|
|
|
// The spot that started it: RI1FJL on 10131.5, an FT8 station in the 30 m data
|
|
// segment. The cluster list showed DATA and the alert announced SSB, because
|
|
// this side's table stopped its CW range at 10.130 and called everything above
|
|
// it phone.
|
|
func TestInferModeDoesNotCallThirtyMetresSSB(t *testing.T) {
|
|
if got := InferMode("", 10_131_500); got != "DATA" {
|
|
t.Errorf("10131.5 kHz → %q, want DATA (there is no SSB on 30 m)", got)
|
|
}
|
|
// And the CW half of the same band still reads as CW.
|
|
if got := InferMode("", 10_110_000); got != "CW" {
|
|
t.Errorf("10110 kHz → %q, want CW", got)
|
|
}
|
|
}
|
|
|
|
// The comment beats the band plan. A spotter who names the mode knows something
|
|
// no table does — an FT8 station calling outside the usual watering hole, a CW
|
|
// operation in a data segment during a contest.
|
|
func TestInferModeTrustsTheCommentFirst(t *testing.T) {
|
|
cases := map[string]struct {
|
|
comment string
|
|
freqHz int64
|
|
want string
|
|
}{
|
|
"FT8 said outright": {"-14 dB FT8", 14_200_000, "FT8"},
|
|
"CW in a data segment": {"CW 599", 14_080_000, "CW"},
|
|
"RBN reports WPM not CW": {"18 dB 25 WPM CQ", 14_080_000, "CW"},
|
|
"phone below the split": {"SSB net", 7_010_000, "SSB"},
|
|
}
|
|
for name, c := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
if got := InferMode(c.comment, c.freqHz); got != c.want {
|
|
t.Errorf("InferMode(%q, %d) = %q, want %q", c.comment, c.freqHz, got, c.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// The watering holes are listed before the wide data blocks they sit inside, and
|
|
// the table is scanned in order — so sorting it by frequency would quietly turn
|
|
// every FT8 hole into "DATA". This is what would fail if somebody did.
|
|
func TestBandPlanKeepsTheWateringHolesAheadOfTheDataBlocks(t *testing.T) {
|
|
holes := map[int64]string{
|
|
3_573_500: "FT8",
|
|
7_074_500: "FT8",
|
|
7_048_000: "FT4",
|
|
14_074_500: "FT8",
|
|
14_080_500: "FT4",
|
|
21_074_500: "FT8",
|
|
28_074_500: "FT8",
|
|
50_313_500: "FT8",
|
|
}
|
|
for hz, want := range holes {
|
|
if got := modeFromFrequency(hz); got != want {
|
|
t.Errorf("%d Hz → %q, want %q", hz, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nothing is claimed for a frequency the plan does not cover. Answering "SSB"
|
|
// for whatever fell through is the exact fault this file exists to fix.
|
|
func TestModeFromFrequencyIsSilentWhenItDoesNotKnow(t *testing.T) {
|
|
for _, hz := range []int64{500_000, 6_000_000, 70_200_000, 432_100_000} {
|
|
if got := modeFromFrequency(hz); got != "" {
|
|
t.Errorf("%d Hz → %q, want \"\" (outside the plan)", hz, got)
|
|
}
|
|
}
|
|
}
|