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:
+56
-3
@@ -416,16 +416,18 @@ func (a *App) autoCallJudge(inst, key string, at time.Time, tr int, buf []acDeco
|
||||
status[st.Call+"|"+st.Band+"|"+st.Mode] = st
|
||||
}
|
||||
|
||||
chase := a.autoCallChase()
|
||||
cands := make([]autocall.Candidate, 0, len(uniq))
|
||||
for _, dd := range uniq {
|
||||
st := status[dd.d.Call+"|"+dd.d.Band+"|"+dd.d.Mode]
|
||||
c := candidateOf(dd.d, st)
|
||||
c := candidateOf(dd.d, st, chase)
|
||||
c.Watched = a.autoCallWatched(dd.d.Call)
|
||||
c.Hidden = a.autoCallHidden(dd.d.Call)
|
||||
cands = append(cands, c)
|
||||
}
|
||||
|
||||
tx := a.autoCallTX()
|
||||
|
||||
act := a.autoCallEngine().OnPeriod(autocall.Period{
|
||||
Instance: inst, Key: key, At: at, TRPeriod: tr, Decodes: cands, TX: tx,
|
||||
MyCall: a.opCall,
|
||||
@@ -437,8 +439,14 @@ func (a *App) autoCallJudge(inst, key string, at time.Time, tr int, buf []acDeco
|
||||
//
|
||||
// Split out and kept pure because ONE line of it was wrong for weeks and
|
||||
// nothing could catch it: the entity's verdict was read as the station's.
|
||||
func candidateOf(d autocall.Decode, st SpotStatus) autocall.Candidate {
|
||||
return autocall.Candidate{
|
||||
// chaseExtras is what the operator hunts BESIDES entities — the cluster's
|
||||
// orthogonal markers, from the same switches that decide whether the badges are
|
||||
// shown at all (Settings → DX Cluster). One answer for the eye and the
|
||||
// transmitter: a marker withdrawn from the screen is not one to call for.
|
||||
type chaseExtras struct{ pota, grid, pfx, county, state bool }
|
||||
|
||||
func candidateOf(d autocall.Decode, st SpotStatus, ch chaseExtras) autocall.Candidate {
|
||||
c := autocall.Candidate{
|
||||
Decode: d,
|
||||
// The ENTITY's verdict decides what is still needed…
|
||||
Need: autoCallNeedOf(st.Status),
|
||||
@@ -455,6 +463,51 @@ func candidateOf(d autocall.Decode, st SpotStatus) autocall.Candidate {
|
||||
// less than the same need never worked at all.
|
||||
Unconfirmed: st.UnconfStatus,
|
||||
}
|
||||
// NOTHING LEFT ON THE ENTITY, AND STILL WORTH A CALL.
|
||||
//
|
||||
// A prefix, a square, a county, a state, a park: never worked, orthogonal to
|
||||
// the entity's verdict, and exactly what the operator ticked in the chase
|
||||
// settings. They ranked at nothing-needed, so auto-call sat through a
|
||||
// never-worked WPX prefix calling CQ and did not answer it.
|
||||
//
|
||||
// Only when the entity has nothing to add — a new band that is ALSO a new
|
||||
// prefix is a new band, and says so.
|
||||
if c.Need == autocall.NeedNone {
|
||||
switch {
|
||||
case ch.pfx && st.NewPfx:
|
||||
c.Need, c.Extra, c.Unconfirmed = autocall.NeedExtra, "prefix", st.UnconfPfx
|
||||
case ch.county && st.NewCounty:
|
||||
c.Need, c.Extra, c.Unconfirmed = autocall.NeedExtra, "county", st.UnconfCty
|
||||
case ch.state && st.NewState:
|
||||
c.Need, c.Extra, c.Unconfirmed = autocall.NeedExtra, "state", st.UnconfState
|
||||
case ch.grid && st.NewGrid:
|
||||
c.Need, c.Extra, c.Unconfirmed = autocall.NeedExtra, "square", st.GridState == "unconf"
|
||||
case ch.pota && st.NewPOTA:
|
||||
c.Need, c.Extra = autocall.NeedExtra, "park"
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// autoCallChase reads the chase switches the cluster and the decode list use.
|
||||
//
|
||||
// Read once per period rather than per decode: they are settings-store reads,
|
||||
// and the period loop runs over every station on the band.
|
||||
func (a *App) autoCallChase() chaseExtras {
|
||||
on := func(key string) bool {
|
||||
if a.settings == nil {
|
||||
return true
|
||||
}
|
||||
v, _ := a.settings.Get(a.ctx, "ui.opslog."+key)
|
||||
return v != "0" // unset means on, as it does on the screen
|
||||
}
|
||||
return chaseExtras{
|
||||
pota: on("chasePota"),
|
||||
grid: on("chaseGrids"),
|
||||
pfx: on("chasePfx"),
|
||||
county: on("chaseCounty"),
|
||||
state: on("chaseState"),
|
||||
}
|
||||
}
|
||||
|
||||
// autoCallDo carries out a decision and records it.
|
||||
|
||||
Reference in New Issue
Block a user