fix(sat): changing satellite mid-pass moves the radio to it
Two birds are often up at once, and switching between them left the frequencies on the first. The selection on the page is the DISPLAY's; the tracker held its own name and went on following whatever it was started with. The way through was to stop tracking and start it again, which is how it was found — and which is also how a Flex throws away and rebuilds both its slices for no reason. RetargetSatelliteTracking changes what is being followed without letting go of the radio or the rotator. Everything derived from the old satellite is cleared so the next step sets it afresh: the frequencies, the mode on both slices (set once per satellite, not per tick), the antennas and the CTCSS tone — the new bird may be U/V where the old one was V/U, which swaps which slice sits on which band. And it happens at once rather than at the next tick. The loop gained a wake channel: a second of the previous satellite's frequencies is a second of the wrong pass, and the antenna would otherwise wait for the new bird to drift a step away from where the old one happened to be. Selecting a satellite with the radio idle is unchanged — a look, not a command. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
@@ -77,6 +77,10 @@ type satTracker struct {
|
||||
|
||||
stop chan struct{}
|
||||
done chan struct{}
|
||||
// wake makes the loop take a step NOW instead of at the next tick. Changing
|
||||
// satellite has to move the radio at once: a second of the old bird's
|
||||
// frequencies is a second of the wrong pass.
|
||||
wake chan struct{}
|
||||
}
|
||||
|
||||
// SatTrackStatus is what the tracker is doing, for the panel.
|
||||
@@ -129,6 +133,7 @@ func (a *App) StartSatelliteTracking(name string, transponder int) error {
|
||||
nominalDown: b.Transponders[transponder].Centre(),
|
||||
stop: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
wake: make(chan struct{}, 1),
|
||||
}
|
||||
t.status = SatTrackStatus{On: true, Name: b.Name, Transponder: b.Transponders[transponder].Label, Mode: b.Transponders[transponder].Mode}
|
||||
|
||||
@@ -259,6 +264,7 @@ func (a *App) satTrackLoop(t *satTracker) {
|
||||
select {
|
||||
case <-t.stop:
|
||||
return
|
||||
case <-t.wake:
|
||||
case <-tick.C:
|
||||
}
|
||||
}
|
||||
@@ -705,3 +711,60 @@ func satSidebands(tp sat.Transponder) (downMode, upMode string) {
|
||||
}
|
||||
return "USB", "USB"
|
||||
}
|
||||
|
||||
// RetargetSatelliteTracking points the tracker at a different satellite without
|
||||
// letting go of the radio.
|
||||
//
|
||||
// Two birds are often up at once, and an operator switching between them found
|
||||
// the frequencies stayed on the first: the panel's selection is the DISPLAY's,
|
||||
// while the tracker held its own name and went on following what it was started
|
||||
// with. Stopping and starting worked, which is how it was discovered, and is
|
||||
// also how a Flex loses and rebuilds both its slices for no reason.
|
||||
//
|
||||
// So the radio stays armed and the rotator stays open, and only what is being
|
||||
// followed changes. Everything derived from the old satellite is cleared so the
|
||||
// next step sets it afresh: the frequencies, the mode on both slices (set once
|
||||
// per satellite, not per tick), the antennas and the tone — the new bird may be
|
||||
// U/V where the old one was V/U, which swaps which slice is on which band.
|
||||
func (a *App) RetargetSatelliteTracking(name string, transponder int) error {
|
||||
a.satTrackMu.Lock()
|
||||
t := a.satTrack
|
||||
a.satTrackMu.Unlock()
|
||||
if t == nil {
|
||||
// Not tracking: this is simply a start.
|
||||
return a.StartSatelliteTracking(name, transponder)
|
||||
}
|
||||
|
||||
_, birds, _ := a.satParts()
|
||||
b, ok := birds.Find(name)
|
||||
if !ok || len(b.Transponders) == 0 {
|
||||
return fmt.Errorf("%s has no frequency plan to tune to", name)
|
||||
}
|
||||
if transponder < 0 || transponder >= len(b.Transponders) {
|
||||
transponder = 0
|
||||
}
|
||||
tp := b.Transponders[transponder]
|
||||
|
||||
t.mu.Lock()
|
||||
t.name, t.tp = b.Name, transponder
|
||||
t.nominalDown = tp.Centre()
|
||||
// Zeroed so the next step tunes and sets the mode again rather than deciding
|
||||
// nothing has changed.
|
||||
t.lastDown, t.lastUp, t.fails = 0, 0, 0
|
||||
// And so the antenna is commanded at once instead of waiting for the new
|
||||
// satellite to drift a step away from where the old one happened to be.
|
||||
t.rotSent = false
|
||||
t.status.Name, t.status.Transponder, t.status.Mode = b.Name, tp.Label, tp.Mode
|
||||
t.status.Error = ""
|
||||
t.mu.Unlock()
|
||||
|
||||
if a.cat != nil && a.cat.SatCapable() {
|
||||
a.applySatRadio(tp)
|
||||
}
|
||||
select {
|
||||
case t.wake <- struct{}{}:
|
||||
default: // a step is already pending; it will pick this up
|
||||
}
|
||||
applog.Printf("sat: now tracking %s (%s)", b.Name, tp.Label)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user