feat(sat): Doppler tracking on the radio
The hard part of satellite tuning is not the arithmetic, it is deciding who owns the dial. A tracker that forces both frequencies fights the operator every time they turn the knob to follow a station across a linear transponder; one that never touches the receiver leaves them chasing a signal that slides nine kilohertz across a 70 cm pass. So the operator owns the receiver and the tracker follows them. Every second it asks the radio where the receiver actually is. Where it put it, nothing has changed. Further than a dial-turn's tolerance, and the operator has chosen a station: what they landed on is converted back into a nominal frequency, and the transmitter is derived from that. Which is the division of labour on a linear bird — the operator listens, the radio does the sums. Three ways to reach the radio, because a satellite pair is a shape of operating rather than a manufacturer's feature. An IC-9700 or IC-9100 is asked for its OWN satellite mode: it pairs main and sub, gives full duplex, and keeps the dials linked the way its designers meant, which is always better than an imitation built out of split. A Flex gets two slices, A the downlink and B the uplink, created when missing, because "slice B does not exist" is not something to make an operator fix at the start of a ten-minute pass. Everything else gets the downlink, and is told so — half the job announced beats half the job hidden. What goes in the log is the NOMINAL pair. Two stations working each other through a transponder read different numbers off their dials at the same instant; the only figure they can both agree on is the transponder's own. FREQ is the uplink and FREQ_RX the downlink — the one place a satellite QSO differs from every other kind, and the reason FREQ alone cannot describe one.
This commit is contained in:
@@ -893,6 +893,10 @@ type App struct {
|
||||
satStore *sat.Store // orbital elements, by satellite name
|
||||
satBirds *sat.Birds // uplink/downlink plan
|
||||
satFetch *sat.Fetcher // element feeds + the on-disk cache
|
||||
// satTrack is the live tracker: the goroutine that walks the radio through a
|
||||
// pass. nil when nothing is being tracked.
|
||||
satTrackMu sync.Mutex
|
||||
satTrack *satTracker
|
||||
|
||||
cwMu sync.Mutex // guards the CW decoder lifecycle
|
||||
cwStop chan struct{} // stops the CW decoder capture loop; nil when off
|
||||
@@ -1960,6 +1964,10 @@ func (a *App) shutdown(ctx context.Context) {
|
||||
applog.Printf("shutdown: closing autostart programs")
|
||||
a.CloseAutostartPrograms()
|
||||
a.stopPSKTarget() // one TLS socket to a public broker; nothing to flush
|
||||
// Before CAT goes down: disarming satellite mode takes the radio out of full
|
||||
// duplex and puts the transmitter back where the operator is listening, and
|
||||
// that has to happen while the link is still up.
|
||||
a.StopSatelliteTracking()
|
||||
applog.Printf("shutdown: stopping UDP")
|
||||
if a.udp != nil {
|
||||
a.udp.StopAll()
|
||||
@@ -3086,6 +3094,10 @@ func (a *App) AddQSO(q qso.QSO) (id int64, err error) {
|
||||
}
|
||||
}()
|
||||
a.applyStationDefaults(&q, true)
|
||||
// Before fillRXDefaults, which copies the transmit frequency into the receive
|
||||
// one: on a satellite the two are on different bands, and letting that copy
|
||||
// happen first would bury the downlink under the uplink.
|
||||
a.applySatellite(&q)
|
||||
fillRXDefaults(&q)
|
||||
fillDistance(&q)
|
||||
a.applyDXCCNumber(&q)
|
||||
@@ -16789,6 +16801,9 @@ func (a *App) reloadAfterProfileSwitch() {
|
||||
// of the process.
|
||||
a.disarmAutoCall("profile switch")
|
||||
a.autoCallEngine().Reset()
|
||||
// Same reasoning for the satellite tracker: it transmits, and the new profile
|
||||
// may be a different station on a different antenna.
|
||||
a.StopSatelliteTracking()
|
||||
}
|
||||
|
||||
// DuplicateProfile clones an existing profile under newName. Useful when
|
||||
|
||||
Reference in New Issue
Block a user