fix: persist CTun center + save VHF/UHF bands (Pluto); QO-100 transverter template

CTun position was lost on band restore / restart, and VHF/UHF bands were
never saved on Pluto.

- SaveCurrentBand: the >61 MHz guard (HPSDR HF-only) skipped saving native
  Pluto VHF/UHF bands → 2m/70cm reverted to defaults. Now HPSDR-only; Pluto
  saves VU-plan bands (FreqToBandIdx guards cross-band pollution).
- Persist DDC center for CTun: new TBandSettings.CenterHz (+ JSON center_hz)
  and TXvtrEntry.LastCenterHz (+ last_center_hz). RestoreBand / ActivateXvtr
  restore the saved center when CTun is on and the offset fits the span,
  instead of forcing center := VFO (which re-centered the receiver).
- UpdateFreqDisplayMax: freq-display upper bound from active transverter's
  FreqEnd (lets QO-100 show 10 GHz past the 6 GHz Pluto cap).
- QO-100 transverter template (slot 2, disabled): downlink 10489.5–10490.0,
  LO 9750. MigrateXvtrTemplate injects it into the first free slot for
  devices that already have a saved xvtr config (template was otherwise
  masked by the loaded slots).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 22:02:01 +03:00
co-authored by Claude Opus 4.8
parent 24f74357cd
commit 40dca01c40
3 changed files with 97 additions and 20 deletions
+24 -13
View File
@@ -534,6 +534,7 @@ type
WidebandFill, SpectrumFill: Boolean);
procedure ApplyFPS(FPS: Integer);
procedure ApplyFreqMhzDigits(Digits: Integer);
procedure UpdateFreqDisplayMax; // верхняя граница FreqDisplay (XVTR/Pluto/HF)
published
// Обработчики событий формы — должны быть в published для RTTI/LFM
@@ -1188,6 +1189,7 @@ begin
for i := 0 to CFG_XVTR_COUNT - 1 do
if BtnXvtrBand[i] <> nil then
StyleButton(BtnXvtrBand[i], i = FController.FCurrentXvtr);
UpdateFreqDisplayMax; // трансвертер может быть >6 ГГц (QO-100)
// Wideband-вид + web-зеркало xvtr (ранее в обёртках Activate/Deactivate).
// rfXvtr эмитят ТОЛЬКО ActivateXvtr/DeactivateXvtr — состояние финальное.
UpdateWidebandFrequencyView;
@@ -5539,30 +5541,39 @@ begin
end;
procedure TMainForm.ApplyFreqMhzDigits(Digits: Integer);
const
MaxFreqs: array[3..5] of Int64 = (999999999, 9999999999, 99999999999);
begin
Digits := EnsureRange(Digits, 3, 5);
FFreqMhzDigits := Digits;
FController.FLoadedGlobal.FreqMhzDigits := Digits; // зеркало в persist-буфер контроллера
FreqDispA.MinMhzDigits := Digits;
FreqDispB.MinMhzDigits := Digits;
// Pluto/VHF/UHF: верхняя граница из Caps (до 6 ГГц), иначе HF по числу цифр.
if FController.IsPluto then
begin
FreqDispA.MaxFreq := Round(FController.BackendCaps.MaxFreqHz);
FreqDispB.MaxFreq := Round(FController.BackendCaps.MaxFreqHz);
end
else
begin
FreqDispA.MaxFreq := MaxFreqs[Digits];
FreqDispB.MaxFreq := MaxFreqs[Digits];
end;
UpdateFreqDisplayMax; // верхняя граница: активный XVTR / Pluto Caps / HF по цифрам
if Assigned(FWebServer) then
FWebServer.FreqMhzDigits := Digits;
LayoutTopVfoBlock;
end;
procedure TMainForm.UpdateFreqDisplayMax;
// Верхняя граница FreqDisplay. Активный трансвертер (XVTR/QO-100) может показывать
// частоты выше диапазона железа (напр. QO-100 — 10.5 ГГц) → берём его FreqEnd.
// Иначе Pluto — из Caps (до 6 ГГц), HPSDR — по числу MHz-цифр.
const
MaxFreqs: array[3..5] of Int64 = (999999999, 9999999999, 99999999999);
var
hi: Int64; xi: Integer;
begin
xi := FController.FCurrentXvtr;
if (xi >= 0) and (xi < CFG_XVTR_COUNT) then
hi := Round(FController.FXvtrSettings.Entries[xi].FreqEnd) + 1000000 // +1 МГц запас
else if FController.IsPluto then
hi := Round(FController.BackendCaps.MaxFreqHz)
else
hi := MaxFreqs[EnsureRange(FFreqMhzDigits, 3, 5)];
if hi < 1000000 then hi := MaxFreqs[3];
if FreqDispA <> nil then FreqDispA.MaxFreq := hi;
if FreqDispB <> nil then FreqDispB.MaxFreq := hi;
end;
procedure TMainForm.BtnSettingsClick(Sender: TObject);
var
SF: TSettingsForm;