diff --git a/app.go b/app.go index 6cbd373..b7ba3cb 100644 --- a/app.go +++ b/app.go @@ -14722,12 +14722,10 @@ func (a *App) startUltrabeam() { } a.motorAnt = c _ = a.motorAnt.Start() - if s.Follow { - stop := make(chan struct{}) - a.ubFollowStop = stop - applog.Printf("ultrabeam: follow loop starting — covered bands %v, step %d kHz", s.Bands, s.StepKHz) - go a.ultrabeamFollowLoop(a.motorAnt, s.StepKHz, s.Bands, stop) - } + // One place starts the follow loop, whether the antenna just connected or the + // operator flipped tracking from the Station Control widget. Two copies of + // this drifting apart is how a step change quietly stops taking effect. + a.restartMotorFollow(s) if s.TXInhibit { stop := make(chan struct{}) a.motorInhibStop = stop @@ -15122,9 +15120,45 @@ func (a *App) SetMotorFollow(on bool, stepKHz int) error { return fmt.Errorf("step must be 25, 50 or 100 kHz") } s.Follow = on - // SaveUltrabeamSettings restarts the client, which is what actually starts or - // stops the follow loop — the setting alone would not. - return a.SaveUltrabeamSettings(s) + + // Persist WITHOUT the restart. SaveUltrabeamSettings tears the client down and + // dials again, which is right when the transport changed and absurd here: the + // operator toggling tracking watched the antenna drop off and reconnect, and + // on a remote controller that is several seconds of a link that was working. + // + // The follow loop is a goroutine with its own stop channel, so it can be + // replaced on its own — the connection underneath never knows. + // Only the two keys this call actually changes. Writing the whole block would + // mean re-normalising host, port, baud and bands to change a checkbox, and + // every one of those is a chance to alter something nobody asked to alter. + if a.settings == nil { + return fmt.Errorf("db not initialized") + } + if err := a.settings.Set(a.ctx, keyUltrabeamFollow, boolStr(s.Follow)); err != nil { + return err + } + if err := a.settings.Set(a.ctx, keyUltrabeamStep, strconv.Itoa(s.StepKHz)); err != nil { + return err + } + a.restartMotorFollow(s) + return nil +} + +// restartMotorFollow swaps the follow loop for one matching the settings, leaving +// the antenna connection alone. +func (a *App) restartMotorFollow(s UltrabeamSettings) { + if a.ubFollowStop != nil { + close(a.ubFollowStop) + a.ubFollowStop = nil + } + if !s.Follow || a.motorAnt == nil { + applog.Printf("ultrabeam: follow loop stopped") + return + } + stop := make(chan struct{}) + a.ubFollowStop = stop + applog.Printf("ultrabeam: follow loop restarting — covered bands %v, step %d kHz", s.Bands, s.StepKHz) + go a.ultrabeamFollowLoop(a.motorAnt, s.StepKHz, s.Bands, stop) } // UltrabeamRetract retracts all elements (storage / safe position).