fix(autocall): rest in overs, no phantom miss, and a readable auto readout
The rest between two series was two minutes — four overs on FT8, by which time the DX has worked four other callers and half the time has gone. It is now counted in the station's OWN overs and is one by default: seven calls, one over listened through, and it goes again if the station is still there. The deadline lands mid-cycle on purpose, so "sit out one over" means one over rather than depending on a millisecond of clock skew. A period the target was DECODED in is never counted as a miss. A period is judged more than once — decodes arrive in a burst and stragglers follow — and a later judgement holds a partial view of it, not evidence of absence: D2ACE answered in the very period the counter then read as a miss. The readout moved out of the Auto button and beside it. "Auto D2ACE 1/7 ·1/3" read as one number, and the control changed width every period. The callsign is now the biggest thing on the row, the calls and the missed periods each carry a label, the miss count appears only once there is one, and a station being waited for gets its own amber chip.
This commit is contained in:
+27
-7
@@ -30,7 +30,10 @@ const (
|
||||
keyAutoCallWatched = "autocall.watched_attempts"
|
||||
keyAutoCallMisses = "autocall.misses"
|
||||
keyAutoCallRounds = "autocall.max_rounds"
|
||||
keyAutoCallRestMin = "autocall.rest_min"
|
||||
// Periods, not minutes — see autocall.Settings.RestPeriods. A new key rather
|
||||
// than a reinterpreted one: the old value was in minutes, and reading "2" as
|
||||
// two periods or as two minutes are eight periods apart.
|
||||
keyAutoCallRest = "autocall.rest_periods"
|
||||
keyAutoCallOnScreen = "autocall.on_screen_only"
|
||||
keyAutoCallTrace = "autocall.trace"
|
||||
)
|
||||
@@ -48,9 +51,10 @@ type AutoCallSettings struct {
|
||||
// it, before it is given up on.
|
||||
Misses int `json:"misses"`
|
||||
// MaxRounds: how many series of calls one station gets in a session, and
|
||||
// RestMin the pause between two of them.
|
||||
MaxRounds int `json:"max_rounds"`
|
||||
RestMin int `json:"rest_min"`
|
||||
// RestPeriods the pause between two of them, counted in the station's own
|
||||
// transmit periods.
|
||||
MaxRounds int `json:"max_rounds"`
|
||||
RestPeriods int `json:"rest_periods"`
|
||||
// OnScreenOnly: call only what the decodes panel is showing, so its filters
|
||||
// steer the transmitter as well as the eye.
|
||||
OnScreenOnly bool `json:"on_screen_only"`
|
||||
@@ -61,6 +65,16 @@ type AutoCallSettings struct {
|
||||
Trace bool `json:"trace"`
|
||||
}
|
||||
|
||||
// intOr reads a stored integer, keeping a stored ZERO — which num() cannot,
|
||||
// since it treats zero as "nothing set".
|
||||
func intOr(v string, def int) int {
|
||||
n, err := strconv.Atoi(strings.TrimSpace(v))
|
||||
if err != nil || n < 0 {
|
||||
return def
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (a *App) GetAutoCallSettings() AutoCallSettings {
|
||||
d := autocall.Defaults()
|
||||
num := func(key string, def int) int {
|
||||
@@ -82,7 +96,9 @@ func (a *App) GetAutoCallSettings() AutoCallSettings {
|
||||
Trace: a.settingOr(keyAutoCallTrace, "0") == "1",
|
||||
Misses: num(keyAutoCallMisses, d.Misses),
|
||||
MaxRounds: num(keyAutoCallRounds, d.MaxRounds),
|
||||
RestMin: num(keyAutoCallRestMin, int(d.Rest/time.Minute)),
|
||||
// Zero is meaningful (call it again next period), so it is read directly
|
||||
// rather than through num(), which treats zero as "unset".
|
||||
RestPeriods: intOr(a.settingOr(keyAutoCallRest, ""), d.RestPeriods),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,12 +110,16 @@ func (a *App) SaveAutoCallSettings(s AutoCallSettings) error {
|
||||
for key, v := range map[string]int{
|
||||
keyAutoCallAttempts: s.Attempts, keyAutoCallWatched: s.WatchedAttempts,
|
||||
keyAutoCallMisses: s.Misses, keyAutoCallRounds: s.MaxRounds,
|
||||
keyAutoCallRestMin: s.RestMin,
|
||||
} {
|
||||
if v > 0 {
|
||||
a.setSetting(key, strconv.Itoa(v))
|
||||
}
|
||||
}
|
||||
// Stored even at zero, unlike the counters above: no rest at all is a
|
||||
// setting, not an empty field.
|
||||
if s.RestPeriods >= 0 {
|
||||
a.setSetting(keyAutoCallRest, strconv.Itoa(s.RestPeriods))
|
||||
}
|
||||
a.applyAutoCall()
|
||||
applog.Printf("autocall: %v (only=%q, %d/%d calls, %d misses, %d rounds)",
|
||||
s.Enabled, s.Only, s.Attempts, s.WatchedAttempts, s.Misses, s.MaxRounds)
|
||||
@@ -140,7 +160,7 @@ func (a *App) autoCallSettings() autocall.Settings {
|
||||
Enabled: s.Enabled, Only: s.Only, OnScreenOnly: s.OnScreenOnly,
|
||||
Attempts: s.Attempts, WatchedAttempts: s.WatchedAttempts,
|
||||
Misses: s.Misses, MaxRounds: s.MaxRounds,
|
||||
Rest: time.Duration(s.RestMin) * time.Minute,
|
||||
RestPeriods: s.RestPeriods,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user