feat(sat): the uplink keeps the correction the operator makes
A transponder does not translate by exactly the published difference — the oscillator on board is decades old on some birds and a kilohertz or two out. So an operator who sounds right to themselves comes back off frequency, corrects it on the transmit VFO, and the tracker put it back one second later, every second, for the rest of the pass. Reported on an IC-9700 against HRD, which keeps the shift the operator sets. The tracker already worked this way for the RECEIVER: it reads the dial back and treats a move as the operator choosing a new station. The transmitter had no equivalent — its comment even said so, "derived from the nominal and never argued with". Now it is read back too, and the difference becomes a standing trim on the nominal uplink. Applied to the nominal rather than the corrected frequency, because a translation error is a fixed offset in the uplink band and not something that scales with the Doppler. Read only while not transmitting: mid-over nobody is turning the knob, and on an Icom this read switches to the SUB band and back, which is the same path TuneSatellite already uses to write the uplink and not something to do under a carrier. Kept per satellite AND per transponder, because that is what it belongs to: the error is a property of the hardware in orbit, stable from one pass to the next. Capped at 20 kHz so a bad stored value cannot put the station outside the passband for ever, and shown in the tune panel with a reset — an offset taken silently from the VFO has to be visible, and the VFO alone cannot bring it back to zero once the operator has drifted somewhere wrong. SatTuner gains SatTransmitHz, implemented for the native Icom satellite mode and for the Flex uplink slice; anything else reports nothing and the uplink is left to the arithmetic, as before.
This commit is contained in:
@@ -172,3 +172,41 @@ func TestBandOrderIsByFrequency(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The uplink trim is a fixed offset on the NOMINAL uplink, so the Doppler
|
||||
// correction is computed from the frequency the operator actually transmits on.
|
||||
//
|
||||
// An IC-9700 operator came back off frequency, corrected it on the transmit
|
||||
// VFO, and the tracker overwrote the correction a second later — every second,
|
||||
// for the whole pass. The trim is what survives that.
|
||||
func TestUplinkTrimShiftsTheNominalUplink(t *testing.T) {
|
||||
tp := sat.Transponder{
|
||||
Label: "linear", Mode: "SSB",
|
||||
DownLo: 435_840_000, DownHi: 435_860_000,
|
||||
UpLo: 145_940_000, UpHi: 145_960_000,
|
||||
}
|
||||
centre := tp.Centre()
|
||||
plain := tp.UplinkFor(centre)
|
||||
for _, trim := range []int64{-2000, -100, 0, 100, 2000} {
|
||||
if got := plain + trim; got-plain != trim {
|
||||
t.Errorf("a %+d Hz trim moved the uplink by %+d", trim, got-plain)
|
||||
}
|
||||
}
|
||||
// And it must not touch the downlink: the operator's receiver is their own,
|
||||
// and a trim taken from the transmit VFO has nothing to say about it.
|
||||
if tp.UplinkFor(centre) != plain {
|
||||
t.Error("UplinkFor is not stable")
|
||||
}
|
||||
}
|
||||
|
||||
// The stored trim is capped. A bad value, or a transmit VFO swung across the
|
||||
// band for some other reason, must not become a permanent offset that puts the
|
||||
// station outside the passband on every future pass.
|
||||
func TestUplinkTrimLimitIsWiderThanAnyTransponderError(t *testing.T) {
|
||||
if satUpTrimLimit < 5000 {
|
||||
t.Errorf("the cap is %d Hz — narrower than transponders are known to be off by", satUpTrimLimit)
|
||||
}
|
||||
if satUpTrimLimit > 100_000 {
|
||||
t.Errorf("the cap is %d Hz — wide enough to reach another band", satUpTrimLimit)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user