mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
Fix FM step change from web UI; add FM grid to web spectrum/ruler
Web UI: stepSel.onchange now sends fm_step command to server when in FM mode; option values stay in Hz so tuneStep works unchanged. WebServer: add fm_step command handler and OnFMStep callback. MainForm: wire WebOnFMStep -> SyncWebFMStep -> SetFMStep + SaveCurrentBand. Web spectrum/ruler: vertical FM-step grid lines in drawSpec; FM-aligned ticks and labels in drawRuler with auto label-density (short ticks between labeled lines, measureText-based labelMult to prevent overlap). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -564,6 +564,7 @@ type
|
||||
procedure WebOnFreqA(Hz: Double);
|
||||
procedure WebOnAttn(Idx: Integer);
|
||||
procedure WebOnTun(On_: Boolean);
|
||||
procedure WebOnFMStep(Idx: Integer);
|
||||
procedure WebOnMic(Samples: PSingle; Count: Integer);
|
||||
procedure WebOnXvtrBand(Idx: Integer);
|
||||
// Synchronize-обёртки (выполняются в UI-потоке)
|
||||
@@ -593,6 +594,7 @@ type
|
||||
procedure SyncWebFreqA;
|
||||
procedure SyncWebAttn;
|
||||
procedure SyncWebTun;
|
||||
procedure SyncWebFMStep;
|
||||
// --- CAT callbacks -------------------------------------------------------
|
||||
function CATGetVfoA: Double;
|
||||
function CATGetVfoB: Double;
|
||||
@@ -1257,6 +1259,7 @@ begin
|
||||
FWebServer.OnFreqA := WebOnFreqA;
|
||||
FWebServer.OnAttn := WebOnAttn;
|
||||
FWebServer.OnTun := WebOnTun;
|
||||
FWebServer.OnFMStep := WebOnFMStep;
|
||||
FWebServer.OnWebMic := WebOnMic;
|
||||
FWebServer.OnXvtrBand := WebOnXvtrBand;
|
||||
if FWebEnabled then FWebServer.Start;
|
||||
@@ -5851,6 +5854,19 @@ begin
|
||||
FNetwork.SetStepAtten(FAtten * 10);
|
||||
end;
|
||||
|
||||
procedure TMainForm.WebOnFMStep(Idx: Integer);
|
||||
begin
|
||||
FWebSyncInt := Idx;
|
||||
FWebSyncM := SyncWebFMStep;
|
||||
TThread.Synchronize(nil, FWebSyncM);
|
||||
end;
|
||||
|
||||
procedure TMainForm.SyncWebFMStep;
|
||||
begin
|
||||
SetFMStep(Max(0, Min(FM_STEP_COUNT - 1, FWebSyncInt)));
|
||||
SaveCurrentBand;
|
||||
end;
|
||||
|
||||
procedure TMainForm.WebOnTun(On_: Boolean);
|
||||
begin
|
||||
FWebSyncBool := On_;
|
||||
|
||||
+22
-3
@@ -605,11 +605,28 @@ begin
|
||||
|
||||
'function drawRuler(){const w=rulerCv.clientWidth,h=rulerCv.clientHeight;rctx.fillStyle="#0c1210";rctx.fillRect(0,0,w,h);' +
|
||||
'const st=localCenter-localSpan/2;' +
|
||||
'for(let i=0;i<=8;i++){const x=i*w/8,f=st+i*localSpan/8;rctx.strokeStyle="#2a3a2e";rctx.lineWidth=1;rctx.beginPath();rctx.moveTo(x+.5,0);rctx.lineTo(x+.5,7);rctx.stroke();rctx.fillStyle="#7a9a80";rctx.font="10px monospace";const t=(f/1e6).toFixed(3);rctx.fillText(t,x-14,21);}' +
|
||||
'const isFM=(cur.mode===5),fmStep=FM_STEP_HZ[fmStepIdx];' +
|
||||
'if(isFM&&fmStep>0){' +
|
||||
'const pxPerStep=w*fmStep/localSpan;' +
|
||||
'const labelMult=Math.max(1,Math.ceil((rctx.measureText("000.000").width+6)/pxPerStep));' +
|
||||
'const firstN=Math.ceil(st/fmStep);' +
|
||||
'for(let n=firstN;n*fmStep<=localCenter+localSpan/2+0.5;n++){' +
|
||||
'const f=n*fmStep,x=(f-st)/localSpan*w;if(x<0||x>=w)continue;' +
|
||||
'rctx.strokeStyle="#2a3a2e";rctx.lineWidth=1;rctx.beginPath();rctx.moveTo(x+.5,0);' +
|
||||
'if((n%labelMult)===0){rctx.lineTo(x+.5,7);rctx.stroke();rctx.fillStyle="#7a9a80";rctx.font="10px monospace";const t=(f/1e6).toFixed(3);rctx.fillText(t,x-rctx.measureText(t).width/2,21);}' +
|
||||
'else{rctx.lineTo(x+.5,4);rctx.stroke();}}}' +
|
||||
'else{for(let i=0;i<=8;i++){const x=i*w/8,f=st+i*localSpan/8;rctx.strokeStyle="#2a3a2e";rctx.lineWidth=1;rctx.beginPath();rctx.moveTo(x+.5,0);rctx.lineTo(x+.5,7);rctx.stroke();rctx.fillStyle="#7a9a80";rctx.font="10px monospace";const t=(f/1e6).toFixed(3);rctx.fillText(t,x-14,21);}}' +
|
||||
'const vxr=hzToX(activeVfoHz(),w);rctx.strokeStyle="#60d070";rctx.lineWidth=1;rctx.beginPath();rctx.moveTo(vxr+.5,0);rctx.lineTo(vxr+.5,h);rctx.stroke();}' +
|
||||
|
||||
'function drawSpec(bins){const w=specCv.clientWidth,h=specCv.clientHeight;sctx.fillStyle="#050a05";sctx.fillRect(0,0,w,h);' +
|
||||
'for(let i=1;i<5;i++){const y=i*h/5;sctx.strokeStyle="#121e12";sctx.lineWidth=1;sctx.beginPath();sctx.moveTo(0,y);sctx.lineTo(w,y);sctx.stroke();}' +
|
||||
'if(cur.mode===5&&FM_STEP_HZ[fmStepIdx]>0){' +
|
||||
'const fmSt=FM_STEP_HZ[fmStepIdx],st2=localCenter-localSpan/2;' +
|
||||
'const pxSt=w*fmSt/localSpan,gm=Math.max(1,Math.ceil(4/pxSt));' +
|
||||
'const fn=Math.ceil(st2/fmSt);' +
|
||||
'for(let n=fn;n*fmSt<=localCenter+localSpan/2+0.5;n++){' +
|
||||
'if((n%gm)!==0)continue;const x=(n*fmSt-st2)/localSpan*w;' +
|
||||
'if(x<0||x>=w)continue;sctx.strokeStyle="#121e12";sctx.lineWidth=1;sctx.beginPath();sctx.moveTo(x+.5,0);sctx.lineTo(x+.5,h);sctx.stroke();}}' +
|
||||
'if(!bins?.length){drawMk(sctx,w,h);return;}' +
|
||||
'if(specSm.length!==bins.length)specSm=new Array(bins.length).fill(-130);' +
|
||||
'const avf=activeVfoHz();const[fl,fh]=fltLH(),x1=hzToX(avf+fl,w),x2=hzToX(avf+fh,w);' +
|
||||
@@ -649,8 +666,10 @@ begin
|
||||
'function syncStepSel(){const isFM=(cur.mode===5);const sel=q("stepSel");if(!sel)return;' +
|
||||
'if(isFM&&!inFMStep){lastHFStep=+sel.value||100;sel.innerHTML="";' +
|
||||
'FM_STEP_LBL.forEach((l,i)=>{const o=document.createElement("option");o.value=FM_STEP_HZ[i];o.textContent=l;sel.appendChild(o);});' +
|
||||
'sel.value=FM_STEP_HZ[fmStepIdx];inFMStep=true;}' +
|
||||
'else if(!isFM&&inFMStep){sel.innerHTML=["10 Hz","100 Hz","1 kHz","10 kHz"].map((l,i)=>`<option value="${[10,100,1000,10000][i]}">${l}</option>`).join("");' +
|
||||
'sel.value=FM_STEP_HZ[fmStepIdx];' +
|
||||
'sel.onchange=function(){if(!inFMStep)return;const hz=+sel.value;const idx=FM_STEP_HZ.indexOf(hz);if(idx>=0){fmStepIdx=idx;ws2({cmd:"fm_step",idx:idx});};};' +
|
||||
'inFMStep=true;}' +
|
||||
'else if(!isFM&&inFMStep){sel.onchange=null;sel.innerHTML=["10 Hz","100 Hz","1 kHz","10 kHz"].map((l,i)=>`<option value="${[10,100,1000,10000][i]}">${l}</option>`).join("");' +
|
||||
'sel.value=lastHFStep;inFMStep=false;}' +
|
||||
'else if(isFM){sel.value=FM_STEP_HZ[fmStepIdx];}}' +
|
||||
'function tuneStep(e){const s=+q("stepSel").value||100;if(e.ctrlKey&&e.shiftKey)return 1000000;if(e.ctrlKey)return 1000;if(e.shiftKey)return 10;return s;}' +
|
||||
|
||||
@@ -136,6 +136,7 @@ type
|
||||
TWebCmdFreqA = procedure(Hz: Double) of object;
|
||||
TWebCmdAttn = procedure(Idx: Integer) of object;
|
||||
TWebCmdTun = procedure(On_: Boolean) of object;
|
||||
TWebCmdFMStep = procedure(Idx: Integer) of object;
|
||||
// XVTR-band: web клиент кликнул кнопку трансвертера (Idx 0..CFG_XVTR_COUNT-1).
|
||||
// Idx=-1 — выход в HF.
|
||||
TWebCmdXvtrBand = procedure(Idx: Integer) of object;
|
||||
@@ -244,6 +245,7 @@ type
|
||||
FOnAttn: TWebCmdAttn;
|
||||
FAttnIdx: Integer;
|
||||
FOnTun: TWebCmdTun;
|
||||
FOnFMStep: TWebCmdFMStep;
|
||||
FTuning: Boolean;
|
||||
FDuplex: Boolean;
|
||||
FFreqMhzDigits: Integer; // 3=999MHz, 4=9.999GHz, 5=99.999GHz
|
||||
@@ -334,6 +336,7 @@ type
|
||||
property OnFreqA: TWebCmdFreqA read FOnFreqA write FOnFreqA;
|
||||
property OnAttn: TWebCmdAttn read FOnAttn write FOnAttn;
|
||||
property OnTun: TWebCmdTun read FOnTun write FOnTun;
|
||||
property OnFMStep: TWebCmdFMStep read FOnFMStep write FOnFMStep;
|
||||
property OnWebMic: TWebMicCB read FOnWebMic write FOnWebMic;
|
||||
end;
|
||||
|
||||
@@ -1184,6 +1187,14 @@ begin
|
||||
if Idx > 2 then Idx := 2;
|
||||
FStateLock.Enter; FAttnIdx := Idx; FStateLock.Leave;
|
||||
if Assigned(FOnAttn) then FOnAttn(Idx);
|
||||
end
|
||||
else if Cmd = 'fm_step' then
|
||||
begin
|
||||
Idx := JsonGetInt(Json, 'idx', FFMStepIdx);
|
||||
if Idx < 0 then Idx := 0;
|
||||
if Idx > 3 then Idx := 3;
|
||||
FStateLock.Enter; FFMStepIdx := Idx; FStateLock.Leave;
|
||||
if Assigned(FOnFMStep) then FOnFMStep(Idx);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user