feat: unify band plan across desktop + web UIs

Web band selector was hardcoded to the HF plan, so on Pluto it showed
160m..6m instead of the VHF/UHF plan and highlighted the wrong entry
(band switching itself already worked: web sends idx, controller maps it
via the IsPluto plan). Mirror the sample-rate unification.

Single source of truth = BoardUtils -> controller:
- BoardUtils: TBandInfo/TBandPlanArray + BuildBandPlan(Pluto).
- TRadioController.BandPlan returns BuildBandPlan(IsPluto).

Both UIs derive from it:
- Desktop already plan-aware (RelabelBands + RestoreBand) - unchanged.
- Web: WebServer.SetBands + "bands" in state JSON; hosts push
  BandPlan on connect/startup; JS builds the band selector from
  state.bands (BAND_N/BAND_F now dynamic).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 14:01:23 +03:00
co-authored by Claude Opus 4.8
parent 6b695ce756
commit 4749cf0cbc
6 changed files with 127 additions and 7 deletions
+36 -3
View File
@@ -298,6 +298,8 @@ type
FXvtrBands: TWebXvtrArray; // список enabled XVTR (для web-UI)
FDevices: TWebDeviceArray; // saved+discovered устройства (для web-overlay)
FRatePresets: array of Integer; // sample-rate пресеты текущего бэкенда (для span-кнопок)
FBandNames: array of string; // band-план текущего бэкенда (имена кнопок)
FBandFreqs: array of Double; // band-план: дефолтные частоты (для подсветки selector)
FCurrentXvtr: Integer; // -1 = HF, иначе индекс активного XVTR
FOnXvtrBand: TWebCmdXvtrBand;
@@ -394,6 +396,7 @@ type
procedure SetXvtrBands(const ABands: TWebXvtrArray; ACurrent: Integer);
procedure SetDeviceList(const ADevices: TWebDeviceArray);
procedure SetRatePresets(const ARates: array of Integer);
procedure SetBands(const ANames: array of string; const AFreqs: array of Double);
property OnSpan: TWebCmdSpan read FOnSpan write FOnSpan;
property OnVolume: TWebCmdVolume read FOnVolume write FOnVolume;
property OnWfAGC: TWebCmdWfAGC read FOnWfAGC write FOnWfAGC;
@@ -539,6 +542,10 @@ begin
SetLength(FXvtrBands, 0);
// Bootstrap-набор рейтов (до connect): хост перезапишет под бэкенд (HPSDR/Pluto).
SetRatePresets([48000, 96000, 192000, 384000, 768000, 1536000]);
// Bootstrap band-план (HF): хост перезапишет под бэкенд на connect.
SetBands(['160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m'],
[1840000,3700000,5357000,7100000,10120000,14200000,
18130000,21300000,24940000,28400000,50150000]);
end;
procedure TWebServer.SetXvtrBands(const ABands: TWebXvtrArray; ACurrent: Integer);
@@ -583,6 +590,22 @@ begin
end;
end;
procedure TWebServer.SetBands(const ANames: array of string; const AFreqs: array of Double);
// Band-план текущего бэкенда (из TRadioController). Web строит band-selector из
// него — HPSDR (HF) и Pluto (VHF/UHF) имеют разные планы.
var i: Integer;
begin
FStateLock.Enter;
try
SetLength(FBandNames, Length(ANames));
SetLength(FBandFreqs, Length(AFreqs));
for i := 0 to High(ANames) do FBandNames[i] := ANames[i];
for i := 0 to High(AFreqs) do FBandFreqs[i] := AFreqs[i];
finally
FStateLock.Leave;
end;
end;
destructor TWebServer.Destroy;
begin
Stop;
@@ -1561,7 +1584,7 @@ const
('LSB','USB','DSB','CWL','CWU','FM','AM','SAM');
var
FS: TFormatSettings;
XvtrJson, DevJson, RateJson: string;
XvtrJson, DevJson, RateJson, BandJson: string;
i: Integer;
begin
FS := DefaultFormatSettings;
@@ -1576,6 +1599,16 @@ begin
RateJson := RateJson + IntToStr(FRatePresets[i]);
end;
RateJson := RateJson + ']';
// Сборка массива bands: [{"name":"6m","freq":50150000},...] (band-selector web).
BandJson := '[';
for i := 0 to High(FBandNames) do
begin
if i > 0 then BandJson := BandJson + ',';
BandJson := BandJson +
Format('{"name":"%s","freq":%.0f}',
[JsonEscape(FBandNames[i]), FBandFreqs[i]], FS);
end;
BandJson := BandJson + ']';
// Сборка массива xvtr_bands: [{"idx":0,"name":"2m"},...]
XvtrJson := '[';
for i := 0 to High(FXvtrBands) do
@@ -1613,7 +1646,7 @@ begin
'"status_text":"%s","board_text":"%s","ip_text":"%s","supply_text":"%s",' +
'"pll_text":"%s","rx_text":"%s","tx_text":"%s","seq_text":"%s",' +
'"xvtr_current":%d,"xvtr_bands":%s,"freq_mhz_digits":%d,' +
'"fmstep_idx":%d,"devices":%s,"rate_presets":%s}',
'"fmstep_idx":%d,"devices":%s,"rate_presets":%s,"bands":%s}',
[FFreq, FVfoB, FActiveVfo,
FMode, MODE_N[FMode mod 8],
FFilterIdx, FFilterBW,
@@ -1642,7 +1675,7 @@ begin
JsonEscape(FSupplyText), JsonEscape(FPLLText), JsonEscape(FRXText),
JsonEscape(FTXText), JsonEscape(FSeqText),
FCurrentXvtr, XvtrJson, FFreqMhzDigits,
FFMStepIdx, DevJson, RateJson
FFMStepIdx, DevJson, RateJson, BandJson
], FS);
finally
FStateLock.Leave;