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
|
||||
}
|
||||
|
||||
+4
-2
@@ -6,13 +6,15 @@
|
||||
"The Doppler correction was wrong — by a factor of about 250, and in the wrong direction. The SGP4 library reports a range rate that is not one: the ISS closing at 5.5 km/s came back as +2036 km/s, which moved a 2 m downlink two megahertz instead of three kilohertz, and moved it the wrong way. OpsLog now measures the range rate from the range itself, which cannot disagree with physics. A 2 m downlink shifts about ±3.5 kHz across a pass and a 70 cm one about ±10 kHz, as they should.",
|
||||
"FlexRadio, satellite: the per-band antennas you configured are now applied to the satellite slices. They were not — the entry form applied them on a band change, to the active slice, and a pass never goes through that path. The two slices are on two different bands, so each gets its own: the downlink takes the receive antenna for its band, the uplink the transmit antenna for its. On a station with transverters (XVTA on 2 m, XVTB on 70 cm) the downlink was left on whatever the radio last used, and heard nothing.",
|
||||
"FlexRadio, satellite: on an inverting transponder the uplink is set to LSB and the downlink to USB, instead of USB at both ends. The passband is turned over, so audio transmitted on the wrong sideband comes back through it upside down — which is every attempted contact on FO-29, RS-44 and AO-73.",
|
||||
"FlexRadio, satellite: the CTCSS tone is set on the uplink slice from the satellite's frequency plan. An FM bird does not answer without it, and it is the one setting an operator cannot reach from the front panel once a pass has started."
|
||||
"FlexRadio, satellite: the CTCSS tone is set on the uplink slice from the satellite's frequency plan. An FM bird does not answer without it, and it is the one setting an operator cannot reach from the front panel once a pass has started.",
|
||||
"Changing satellite while tracking now moves the radio to the new one at once, and the antenna with it. The selection on the page was the display's; the tracker held its own and went on following whatever it was started with, so with two birds up at the same time the frequencies stayed on the first — the only way through was to stop tracking and start it again. The radio stays armed through the change, so a Flex no longer throws away and rebuilds both its slices for nothing."
|
||||
],
|
||||
"fr": [
|
||||
"La correction Doppler était fausse — d’un facteur d’environ 250, et dans le mauvais sens. La bibliothèque SGP4 renvoie une vitesse radiale qui n’en est pas une : l’ISS se rapprochant à 5,5 km/s était rapportée à +2036 km/s, ce qui déplaçait une descente 2 m de deux mégahertz au lieu de trois kilohertz, et dans la mauvaise direction. OpsLog mesure désormais cette vitesse à partir de la distance elle-même, ce qui ne peut pas contredire la physique. Une descente 2 m se décale d’environ ±3,5 kHz sur un passage et une 70 cm d’environ ±10 kHz, comme il se doit.",
|
||||
"FlexRadio, satellite : les antennes par bande que vous avez configurées sont désormais appliquées aux tranches satellite. Elles ne l’étaient pas — la fenêtre de saisie les appliquait au changement de bande, sur la tranche active, et un passage ne passe jamais par là. Les deux tranches sont sur deux bandes différentes, donc chacune reçoit la sienne : la descente prend l’antenne de réception de sa bande, la montée l’antenne d’émission de la sienne. Sur une station à transverters (XVTA en 2 m, XVTB en 70 cm), la descente restait sur ce que la radio utilisait en dernier, et n’entendait rien.",
|
||||
"FlexRadio, satellite : sur un transpondeur inverseur, la montée est mise en LSB et la descente en USB, au lieu d’USB des deux côtés. La bande passante est retournée : une audio émise sur la mauvaise bande latérale revient à l’envers — soit tous les QSO tentés sur FO-29, RS-44 et AO-73.",
|
||||
"FlexRadio, satellite : la tonalité CTCSS est réglée sur la tranche de montée depuis le plan de fréquences du satellite. Un satellite FM ne répond pas sans elle, et c’est le seul réglage qu’un OM ne peut pas atteindre en façade une fois le passage commencé."
|
||||
"FlexRadio, satellite : la tonalité CTCSS est réglée sur la tranche de montée depuis le plan de fréquences du satellite. Un satellite FM ne répond pas sans elle, et c’est le seul réglage qu’un OM ne peut pas atteindre en façade une fois le passage commencé.",
|
||||
"Changer de satellite pendant le suivi déplace désormais la radio sur le nouveau immédiatement, et l’antenne avec. La sélection de la page était celle de l’affichage ; le tracker gardait la sienne et continuait de suivre celui avec lequel il avait démarré, donc avec deux satellites en passage simultané les fréquences restaient sur le premier — il fallait arrêter puis relancer le suivi. La radio reste armée pendant le changement : un Flex ne jette plus ses deux tranches pour les reconstruire inutilement."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Satellite as SatIcon, Radio, ArrowUp, ArrowDown, PanelRightClose, Panel
|
||||
import {
|
||||
GetSatelliteBirds, GetSatellitePositions, GetSatellitePasses, GetSatelliteTuning,
|
||||
GetSatelliteGroundTrack, GetSatelliteTLEInfo, GetSatelliteNextPass, GetSatelliteSkyTrack,
|
||||
StartSatelliteTracking, StopSatelliteTracking, GetSatelliteTracking,
|
||||
StartSatelliteTracking, StopSatelliteTracking, GetSatelliteTracking, RetargetSatelliteTracking,
|
||||
} from '../../wailsjs/go/main/App';
|
||||
import { EventsOn } from '../../wailsjs/runtime/runtime';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -397,6 +397,24 @@ export function SatellitePanel({ myGrid }: { myGrid: string }) {
|
||||
} catch (e: any) { setErr(String(e?.message ?? e)); }
|
||||
};
|
||||
|
||||
// Changing satellite WHILE tracking moves the radio to the new one at once.
|
||||
//
|
||||
// The selection here is the display's; the tracker held its own and went on
|
||||
// following what it was started with, so two birds up at the same time meant
|
||||
// switching between them and watching the frequencies stay on the first.
|
||||
// Stopping and restarting worked, and is also how a Flex throws away and
|
||||
// rebuilds both its slices for nothing.
|
||||
//
|
||||
// Guarded on tracking being on, so selecting a satellite with the radio idle
|
||||
// stays what it has always been: a look, not a command.
|
||||
const trackingOn = !!tracking?.on;
|
||||
useEffect(() => {
|
||||
if (!trackingOn || !sel) return;
|
||||
RetargetSatelliteTracking(sel, tpIdx)
|
||||
.then(async () => setTracking((await GetSatelliteTracking()) as any))
|
||||
.catch((e: any) => setErr(String(e?.message ?? e)));
|
||||
}, [sel, tpIdx, trackingOn]);
|
||||
|
||||
// ── Map ──────────────────────────────────────────────────────────────────
|
||||
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
Vendored
+2
@@ -1045,6 +1045,8 @@ export function RestartApp():Promise<void>;
|
||||
|
||||
export function RestartQSORecorder():Promise<void>;
|
||||
|
||||
export function RetargetSatelliteTracking(arg1:string,arg2:number):Promise<void>;
|
||||
|
||||
export function RetryOfflineSync():Promise<number>;
|
||||
|
||||
export function RevealDataFolder():Promise<void>;
|
||||
|
||||
@@ -2022,6 +2022,10 @@ export function RestartQSORecorder() {
|
||||
return window['go']['main']['App']['RestartQSORecorder']();
|
||||
}
|
||||
|
||||
export function RetargetSatelliteTracking(arg1, arg2) {
|
||||
return window['go']['main']['App']['RetargetSatelliteTracking'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function RetryOfflineSync() {
|
||||
return window['go']['main']['App']['RetryOfflineSync']();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user