feat: docked watch-list panel, and auto-call learns the orthogonal markers
The watch list was a tab, and an operator working FT8 lives on the decodes one: a station they had asked to be told about turned up on a screen they were not looking at. The same answer is now docked in the widget strip, above the tabs, reduced to what is worth acting on — on the air and still needed, one row per band and mode, with the cluster's own NEW DXCC / NEW BAND / NEW SLOT badge and a click that tunes. Off by default. The "active and needed" answer costs a debounced query per visible slot, so it is written once (lib/watchlistSpots) and the tab uses it too. Auto-call: - It answers a new prefix, county, state, square or park. Those markers are orthogonal to the entity, they ranked as nothing-needed, and the engine sat through a never-worked WPX prefix calling CQ. New rung at the foot of the ladder, gated by the chase switches the badges use — which meant making those switches portable, since the backend cannot read localStorage. - It calls THROUGH a pileup. Giving up the moment the DX answered somebody else is precisely how a queue is not worked; the call and miss counters already bound the effort, and a station in mid-exchange is still never chosen as a new target. The PSK Reporter panel now follows the station auto-call is waiting for: the analysis takes a history query and a period or two to fill, so starting it when the DX comes free is starting it too late. Callbook lookup: a compound callsign with a page of its OWN keeps that page's location. QRZ files HP/WE9G under exactly that form, with the Panama square the station is operating from, and the rule that drops a home address from a portable call was throwing it away. The record's own country tells an operation's page from a home page. Changelog: entries may open with [NEW], drawn as a pill in the What's new dialog — a release is mostly fixes and the two or three genuinely new things should not have to be found by reading all of it.
This commit is contained in:
+46
-3
@@ -12,12 +12,15 @@ import (
|
||||
// engine refused twenty decodes out of twenty-one as "worked" — a watched
|
||||
// DXpedition calling CQ among them — because the ENTITY's status was read as
|
||||
// the station's.
|
||||
// allChased is the default: every orthogonal marker ticked.
|
||||
var allChased = chaseExtras{pota: true, grid: true, pfx: true, county: true, state: true}
|
||||
|
||||
func TestCandidateWorkedIsTheCallsignNotTheEntity(t *testing.T) {
|
||||
d := autocall.Decode{Call: "J38DX", Band: "10m", Mode: "FT8", IsNew: true}
|
||||
|
||||
// Grenada worked on 10 m FT8, this callsign never worked: nothing is needed
|
||||
// from it, and calling it is NOT a duplicate.
|
||||
c := candidateOf(d, SpotStatus{Status: "worked", WorkedSlot: false})
|
||||
c := candidateOf(d, SpotStatus{Status: "worked", WorkedSlot: false}, allChased)
|
||||
if c.Worked {
|
||||
t.Error("a station never worked was refused because its entity was")
|
||||
}
|
||||
@@ -26,13 +29,53 @@ func TestCandidateWorkedIsTheCallsignNotTheEntity(t *testing.T) {
|
||||
}
|
||||
|
||||
// The same callsign already in the log on this band and mode IS a duplicate.
|
||||
if c := candidateOf(d, SpotStatus{Status: "worked", WorkedSlot: true}); !c.Worked {
|
||||
if c := candidateOf(d, SpotStatus{Status: "worked", WorkedSlot: true}, allChased); !c.Worked {
|
||||
t.Error("a callsign already worked on this band and mode was not flagged")
|
||||
}
|
||||
|
||||
// And a real need still carries through, with the unconfirmed distinction.
|
||||
c = candidateOf(d, SpotStatus{Status: "new-band", UnconfStatus: true})
|
||||
c = candidateOf(d, SpotStatus{Status: "new-band", UnconfStatus: true}, allChased)
|
||||
if c.Need != autocall.NeedBand || !c.Unconfirmed {
|
||||
t.Errorf("new-band unconfirmed came through as %v (unconf=%v)", c.Need, c.Unconfirmed)
|
||||
}
|
||||
}
|
||||
|
||||
// A station whose ENTITY has nothing left to give can still be the reason the
|
||||
// operator is on the band: a WPX prefix, a county, a square, a park that has
|
||||
// never been worked. Reported from the air — auto-call sat through a
|
||||
// never-worked prefix calling CQ and did nothing.
|
||||
func TestOrthogonalMarkersAreWorthACall(t *testing.T) {
|
||||
d := autocall.Decode{Call: "BH2SWB", Band: "10m", Mode: "FT8", IsNew: true}
|
||||
worked := SpotStatus{Status: "worked"}
|
||||
|
||||
for _, tc := range []struct {
|
||||
what string
|
||||
st SpotStatus
|
||||
want string
|
||||
}{
|
||||
{"prefix", SpotStatus{Status: "worked", NewPfx: true}, "prefix"},
|
||||
{"county", SpotStatus{Status: "worked", NewCounty: true}, "county"},
|
||||
{"state", SpotStatus{Status: "worked", NewState: true}, "state"},
|
||||
{"square", SpotStatus{Status: "worked", NewGrid: true}, "square"},
|
||||
{"park", SpotStatus{Status: "worked", NewPOTA: true}, "park"},
|
||||
} {
|
||||
c := candidateOf(d, tc.st, allChased)
|
||||
if c.Need != autocall.NeedExtra || c.Extra != tc.want {
|
||||
t.Errorf("%s: need=%v extra=%q, want extra %q", tc.what, c.Need, c.Extra, tc.want)
|
||||
}
|
||||
// And not when the operator does not chase that kind of thing: the
|
||||
// switch that withdraws the badge withdraws the call with it.
|
||||
if c := candidateOf(d, tc.st, chaseExtras{}); c.Need != autocall.NeedNone {
|
||||
t.Errorf("%s: called for a marker the operator does not chase", tc.what)
|
||||
}
|
||||
}
|
||||
|
||||
// It is the LOWEST rung: a real need on the entity still says what it is.
|
||||
if c := candidateOf(d, SpotStatus{Status: "new-band", NewPfx: true}, allChased); c.Need != autocall.NeedBand {
|
||||
t.Errorf("need = %v — a new band that is also a new prefix is a new band", c.Need)
|
||||
}
|
||||
// Nothing anywhere is still nothing.
|
||||
if c := candidateOf(d, worked, allChased); c.Need != autocall.NeedNone {
|
||||
t.Errorf("need = %v on a station with nothing to gain", c.Need)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user