From 3cedc6225d3ef75090680de91b4612cdfd13621e Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 23 Jun 2026 14:35:56 +0300 Subject: [PATCH] fix: web spectrum tuning range from backend caps; remove dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: dragging/wheeling the spectrum in the web UI clamped frequency to a hardcoded HF ceiling (vfoMax=60 MHz), so on Pluto VHF/UHF it snapped down to 60 MHz and couldn't pan up (only the VFO wheel worked, via a different limit). Push the backend's tuning range (BackendCaps.MinFreqHz/MaxFreqHz) to the web alongside the band plan; JS vfoMin/vfoMax use freq_min/freq_max from state. The transverter branch (XVTR_CUR>=0) is unchanged — it keeps the wide-open range since the displayed RF freq is translated to IF and clamped server-side. Dead-code cleanup (audit of this session's work): - SampleRateMode/TSampleRateMode: orphaned when the sample-rate unification replaced the srmContinuous branch in ApplyBackendCapsToUI; no readers left. Removed (+ now-unused PLUTO_MAX_SR). - Pre-existing write-only/unused: TBackendCaps.MaxSampleRate, SampleRateOverlay.SPAN_NAMES, MainForm.BAND_FREQ. Co-Authored-By: Claude Opus 4.8 --- HPSDRNetwork.pas | 2 -- MainForm.pas | 4 +--- PlutoBackend.pas | 3 --- RadioBackend.pas | 3 --- SampleRateOverlay.pas | 1 - WebPageHtml.pas | 8 ++++++-- WebServer.pas | 23 +++++++++++++++++++++-- ewsdrd.lpr | 1 + 8 files changed, 29 insertions(+), 16 deletions(-) diff --git a/HPSDRNetwork.pas b/HPSDRNetwork.pas index 0af281b..d5114b8 100644 --- a/HPSDRNetwork.pas +++ b/HPSDRNetwork.pas @@ -631,8 +631,6 @@ begin Result.HasRFBandwidth := False; Result.HasFullDuplex := True; Result.MinSampleRate := 48000; - Result.MaxSampleRate := 1536000; - Result.SampleRateMode := srmDiscrete; // Бэкенд — единственный источник своих рейтов (общий для desktop/web UI). SetLength(Result.RatePresets, 6); Result.RatePresets[0] := 48000; diff --git a/MainForm.pas b/MainForm.pas index 1f5524a..38755ce 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -94,9 +94,6 @@ const BAND_NAMES: array[0..BAND_COUNT-1] of string = ( '160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m'); - BAND_FREQ: array[0..BAND_COUNT-1] of Double = ( - 1900000, 3750000, 5357000, 7100000, 10125000, - 14200000, 18120000, 21200000, 24940000, 28500000, 50150000); MODE_NAMES: array[0..MODE_COUNT-1] of string = ( 'LSB','USB','DSB','CWL','CWU','FM','AM','SAM'); @@ -5397,6 +5394,7 @@ begin Freqs[i] := Plan[i].FreqHz; end; FWebServer.SetBands(Names, Freqs); + with FController.BackendCaps do FWebServer.SetFreqRange(MinFreqHz, MaxFreqHz); end; procedure TMainForm.PushXvtrToWeb; diff --git a/PlutoBackend.pas b/PlutoBackend.pas index 629d637..ca99191 100644 --- a/PlutoBackend.pas +++ b/PlutoBackend.pas @@ -139,7 +139,6 @@ implementation const PLUTO_MIN_SR = 521000; // нижний предел AD936x sampling_frequency - PLUTO_MAX_SR = 61440000; PLUTO_MIN_HZ = 46875000; // AD9361 нативно (LibreSDR/AntSDR); AD9363 — 325 МГц PLUTO_MAX_HZ = 6000000000.0; PLUTO_DEF_SR = 1536000; // дефолтный rate если контроллер задал невалидный @@ -389,8 +388,6 @@ begin Result.HasRFBandwidth := True; Result.HasFullDuplex := True; Result.MinSampleRate := PLUTO_MIN_SR; - Result.MaxSampleRate := PLUTO_MAX_SR; - Result.SampleRateMode := srmContinuous; // Пресеты для SampleRateOverlay (все > 520 ksps, минимум AD936x). SetLength(Result.RatePresets, 8); Result.RatePresets[0] := 576000; diff --git a/RadioBackend.pas b/RadioBackend.pas index 3ccaeaa..f8a89f3 100644 --- a/RadioBackend.pas +++ b/RadioBackend.pas @@ -28,7 +28,6 @@ uses type TBackendKind = (bkHPSDR, bkPluto); - TSampleRateMode = (srmDiscrete, srmContinuous); TBackendRateArray = array of Integer; // пресеты sample-rate (общий тип UI) // Описание возможностей бэкенда. Контроллер гейтит UI/поведение по этим полям. @@ -45,8 +44,6 @@ type HasRFBandwidth: Boolean; // настройка аналоговой RF-полосы (Pluto) HasFullDuplex: Boolean; // одновременный RX+TX (Pluto / QO-100) MinSampleRate: Integer; - MaxSampleRate: Integer; - SampleRateMode: TSampleRateMode; RatePresets: TBackendRateArray; // пресеты для SampleRateOverlay MinFreqHz: Double; MaxFreqHz: Double; diff --git a/SampleRateOverlay.pas b/SampleRateOverlay.pas index 8b9fd21..4833f70 100644 --- a/SampleRateOverlay.pas +++ b/SampleRateOverlay.pas @@ -90,7 +90,6 @@ implementation const SPAN_RATES: array[0..5] of Integer = (48000, 96000, 192000, 384000, 768000, 1536000); - SPAN_NAMES: array[0..5] of string = ('48k', '96k', '192k', '384k', '768k', '1536k'); HIDE_W = 22; SPAN_H = 18; diff --git a/WebPageHtml.pas b/WebPageHtml.pas index 36118ad..2283da0 100644 --- a/WebPageHtml.pas +++ b/WebPageHtml.pas @@ -455,6 +455,7 @@ begin 'let stA=0,stAmem=100,stB=0,stBmem=100;' + 'let freqMhzDigits=3,freqMaxHz=999999999;' + + 'let freqMin=30000,freqMax=60000000;' + // диапазон бэкенда (из state.freq_min/max) 'function fmtVfo(hz,step){' + 'const N=freqMhzDigits+6;' + 'const s=String(Math.max(0,Math.round(hz||0))).padStart(N,"0");' + @@ -695,6 +696,7 @@ begin 'if(msg.bands!=null&&Array.isArray(msg.bands)&&msg.bands.length){' + 'const nn=msg.bands.map(function(b){return b.name;});' + 'if(JSON.stringify(nn)!==JSON.stringify(BAND_N)){BAND_N=nn;BAND_F=msg.bands.map(function(b){return b.freq;});rebuildBandSel();}}' + + 'if(msg.freq_min!=null)freqMin=msg.freq_min;if(msg.freq_max!=null)freqMax=msg.freq_max;' + // Обновляем localCenter/localSpan из сервера только когда пользователь не тянет 'if(!dragging&&Date.now()-wheelActiveTs>300){' + // В CTUN без DUP: TX-спектр WDSP центрирован по vfo_a_hz, а не по DDC LO. @@ -813,8 +815,10 @@ begin 'function activeVfoHz(){return activeVfo==="A"?vfoA:vfoB;}' + 'function setActiveVfoHz(f){if(activeVfo==="A")vfoA=f;else vfoB=f;}' + 'function sendActiveFreq(f){if(activeVfo==="A")ws2({cmd:"freq",hz:f});else ws2({cmd:"freq",hz:f});}' + - 'function vfoMin(){return XVTR_CUR>=0?0:30000;}' + - 'function vfoMax(){return XVTR_CUR>=0?999999999999:60000000;}' + + // Границы перестройки берём из бэкенда (freqMin/freqMax из state), а не + // хардкодим HF — иначе на Pluto (VHF/UHF) drag по спектру клампило в 60 МГц. + 'function vfoMin(){return XVTR_CUR>=0?0:Math.max(30000,freqMin);}' + + 'function vfoMax(){return XVTR_CUR>=0?999999999999:freqMax;}' + 'function doWheel(e){e.preventDefault();e.stopPropagation();audioKick();const dir=e.deltaY<0?1:-1,st=tuneStep(e),base=Math.floor(activeVfoHz()/st)*st;' + 'const nf=cl(dir>0?base+st:(Math.round(activeVfoHz())===base?base-st:base),vfoMin(),vfoMax());' + 'setActiveVfoHz(nf);wheelActiveTs=Date.now();' + diff --git a/WebServer.pas b/WebServer.pas index e629f86..b466911 100644 --- a/WebServer.pas +++ b/WebServer.pas @@ -300,6 +300,8 @@ type FRatePresets: array of Integer; // sample-rate пресеты текущего бэкенда (для span-кнопок) FBandNames: array of string; // band-план текущего бэкенда (имена кнопок) FBandFreqs: array of Double; // band-план: дефолтные частоты (для подсветки selector) + FFreqMin: Double; // диапазон перестройки бэкенда (клампинг drag/wheel) + FFreqMax: Double; FCurrentXvtr: Integer; // -1 = HF, иначе индекс активного XVTR FOnXvtrBand: TWebCmdXvtrBand; @@ -397,6 +399,7 @@ type procedure SetDeviceList(const ADevices: TWebDeviceArray); procedure SetRatePresets(const ARates: array of Integer); procedure SetBands(const ANames: array of string; const AFreqs: array of Double); + procedure SetFreqRange(AMin, AMax: Double); property OnSpan: TWebCmdSpan read FOnSpan write FOnSpan; property OnVolume: TWebCmdVolume read FOnVolume write FOnVolume; property OnWfAGC: TWebCmdWfAGC read FOnWfAGC write FOnWfAGC; @@ -546,6 +549,7 @@ begin SetBands(['160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m'], [1840000,3700000,5357000,7100000,10120000,14200000, 18130000,21300000,24940000,28400000,50150000]); + SetFreqRange(0, 61440000); // HPSDR bootstrap; хост перезапишет под бэкенд end; procedure TWebServer.SetXvtrBands(const ABands: TWebXvtrArray; ACurrent: Integer); @@ -606,6 +610,19 @@ begin end; end; +procedure TWebServer.SetFreqRange(AMin, AMax: Double); +// Диапазон перестройки текущего бэкенда (caps): web клампит drag/wheel/click по +// спектру в эти границы. HPSDR 0..61.44МГц / Pluto 46.875МГц..6ГГц. +begin + FStateLock.Enter; + try + FFreqMin := AMin; + FFreqMax := AMax; + finally + FStateLock.Leave; + end; +end; + destructor TWebServer.Destroy; begin Stop; @@ -1646,7 +1663,8 @@ 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,"bands":%s}', + '"fmstep_idx":%d,"devices":%s,"rate_presets":%s,"bands":%s,' + + '"freq_min":%.0f,"freq_max":%.0f}', [FFreq, FVfoB, FActiveVfo, FMode, MODE_N[FMode mod 8], FFilterIdx, FFilterBW, @@ -1675,7 +1693,8 @@ begin JsonEscape(FSupplyText), JsonEscape(FPLLText), JsonEscape(FRXText), JsonEscape(FTXText), JsonEscape(FSeqText), FCurrentXvtr, XvtrJson, FFreqMhzDigits, - FFMStepIdx, DevJson, RateJson, BandJson + FFMStepIdx, DevJson, RateJson, BandJson, + FFreqMin, FFreqMax ], FS); finally FStateLock.Leave; diff --git a/ewsdrd.lpr b/ewsdrd.lpr index e24bc52..5b8966d 100644 --- a/ewsdrd.lpr +++ b/ewsdrd.lpr @@ -314,6 +314,7 @@ begin Freqs[i] := Plan[i].FreqHz; end; FWeb.SetBands(Names, Freqs); + with FCtrl.BackendCaps do FWeb.SetFreqRange(MinFreqHz, MaxFreqHz); end; // --- Входящие web-команды (WS-поток) → маршалинг в главный поток ---