mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
Web TX meter: show power/SWR in S-meter canvas during transmit
- WebServer: add fwd_w, swr, pa_max_power to state JSON and PushSpectrum - MainForm: pass FLastFwdW, FLastSWR, FPAMaxPower to PushSpectrum - Web UI: paintSM switches to paintTXMeter when transmitting/tuning; same layout as desktop — two sub-bars at 44-58% height, power ticks above, SWR ticks below, SWR>2.5 turns red with "SWR High" label Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -412,6 +412,7 @@ begin
|
||||
'let smAvg=-130,smPeak=-130,smMin=-130;' +
|
||||
'function dbToX(db,bx,bw){const DB_MIN=-127,DB_MAX=-13;return Math.max(bx,Math.min(bx+bw,Math.round(bx+(db-DB_MIN)/(DB_MAX-DB_MIN)*bw)));}' +
|
||||
'function paintSM(dbm){' +
|
||||
'if(cur.transmitting||cur.tuning){paintTXMeter();return;}' +
|
||||
'const w=smCv.clientWidth||340,h=smCv.clientHeight||64;' +
|
||||
'const AVG_ALPHA=0.40,ZONE_DB=6,PEAK_BASE=0.12,MIN_BASE=0.12,ACCEL=0.030,MAX_A=0.92;' +
|
||||
'const DBM_MARKS=[-120,-100,-80,-60,-40,-20];' +
|
||||
@@ -460,6 +461,71 @@ begin
|
||||
'mctx.font="8px Courier New";mctx.fillStyle="#90A070";mctx.fillText("dBm",lblZoneL+4+twDbm+2,YT-10);' +
|
||||
'mctx.fillStyle="#F0F0F0";mctx.font="bold 12px Courier New";mctx.fillText(sLbl,lblZoneL+4,YB+14);}' +
|
||||
|
||||
'function paintTXMeter(){' +
|
||||
'const w=smCv.clientWidth||340,h=smCv.clientHeight||64;' +
|
||||
'const fwdW=cur.fwd_w||0,swr=Math.max(1,cur.swr||1),maxW=cur.pa_max_power||100;' +
|
||||
'const SWR_HIGH=2.5,SWR_MAX=10.0,TICK_LONG=5;' +
|
||||
'const swrHigh=swr>SWR_HIGH;' +
|
||||
'mctx.font="bold 12px Courier New";' +
|
||||
'const lblPwr=fwdW.toFixed(0)+"W",lblSWR="SWR:"+swr.toFixed(1);' +
|
||||
'const maxTWPwr=mctx.measureText(maxW.toFixed(0)+"W").width;' +
|
||||
'const maxTWSWR=mctx.measureText("SWR:10.0").width;' +
|
||||
'mctx.font="8px Courier New";' +
|
||||
'const labelNeed=Math.max(maxTWPwr,maxTWSWR)+8;' +
|
||||
'const leftInfo=Math.max(62,labelNeed+4),BX=leftInfo,BW=Math.max(10,w-leftInfo-8),lblZoneL=BX-labelNeed;' +
|
||||
'const YT=Math.round(h*44/100),YB=Math.round(h*58/100);' +
|
||||
'const YM=Math.round((YT+YB)/2);' +
|
||||
'const PY1=YT,PY2=YM-1,SY1=YM+1,SY2=YB;' +
|
||||
'const swarnX=BX+Math.round(Math.max(0,Math.min(1,(SWR_HIGH-1)/(SWR_MAX-1)))*BW);' +
|
||||
'function pwrX(p){return BX+Math.round(Math.max(0,Math.min(1,p))*BW);}' +
|
||||
'function swrX(s){return BX+Math.round(Math.max(0,Math.min(1,(s-1)/(SWR_MAX-1)))*BW);}' +
|
||||
'mctx.clearRect(0,0,w,h);' +
|
||||
// фоновые зоны
|
||||
'mctx.fillStyle="#001F09";mctx.fillRect(BX,PY1,BW,PY2-PY1);' +
|
||||
'mctx.fillStyle="#001F09";mctx.fillRect(BX,SY1,swarnX-BX,SY2-SY1);' +
|
||||
'mctx.fillStyle="#281509";mctx.fillRect(swarnX,SY1,BX+BW-swarnX,SY2-SY1);' +
|
||||
// бар мощности
|
||||
'const pEnd=pwrX(maxW>0?fwdW/maxW:0);' +
|
||||
'if(pEnd>BX){mctx.fillStyle="#44CC00";mctx.fillRect(BX,PY1+1,pEnd-BX,PY2-PY1-2);}' +
|
||||
// бар КСВ
|
||||
'const sEnd=swrX(swr);' +
|
||||
'if(sEnd>BX){' +
|
||||
'if(!swrHigh){mctx.fillStyle="#FFAA00";mctx.fillRect(BX,SY1+1,sEnd-BX,SY2-SY1-2);}' +
|
||||
'else{const safeEnd=Math.min(swarnX,sEnd);' +
|
||||
'if(safeEnd>BX){mctx.fillStyle="#FFAA00";mctx.fillRect(BX,SY1+1,safeEnd-BX,SY2-SY1-2);}' +
|
||||
'if(sEnd>swarnX){mctx.fillStyle="#CC0000";mctx.fillRect(swarnX,SY1+1,sEnd-swarnX,SY2-SY1-2);}}}' +
|
||||
// делитель 2.5
|
||||
'mctx.strokeStyle="#404040";mctx.lineWidth=1;mctx.beginPath();mctx.moveTo(swarnX,SY1);mctx.lineTo(swarnX,SY2);mctx.stroke();' +
|
||||
// рамки
|
||||
'mctx.lineWidth=1;mctx.strokeStyle="#283030";mctx.strokeRect(lblZoneL,PY1,BX+BW-lblZoneL,PY2-PY1);' +
|
||||
'mctx.strokeStyle=swrHigh?"#CC0000":"#283030";mctx.strokeRect(lblZoneL,SY1,BX+BW-lblZoneL,SY2-SY1);' +
|
||||
// разделители зона надписей / шкала
|
||||
'mctx.strokeStyle="#404040";mctx.lineWidth=1;' +
|
||||
'mctx.beginPath();mctx.moveTo(BX,PY1+1);mctx.lineTo(BX,PY2-1);mctx.stroke();' +
|
||||
'mctx.beginPath();mctx.moveTo(BX,SY1+1);mctx.lineTo(BX,SY2-1);mctx.stroke();' +
|
||||
// тики мощности выше power-бара
|
||||
'mctx.font="8px Courier New";' +
|
||||
'const pwrMarks=[0,0.25,0.5,0.75,1.0];' +
|
||||
'for(let i=0;i<pwrMarks.length;i++){const x=pwrX(pwrMarks[i]);' +
|
||||
'mctx.strokeStyle="#DCE0E0";mctx.lineWidth=1;mctx.beginPath();mctx.moveTo(x,PY1-TICK_LONG);mctx.lineTo(x,PY1-1);mctx.stroke();' +
|
||||
'const t=(pwrMarks[i]*maxW).toFixed(0),tw=mctx.measureText(t).width;' +
|
||||
'mctx.fillStyle="#DCDCD0";mctx.fillText(t,Math.round(x-tw/2),PY1-TICK_LONG-3);}' +
|
||||
// тики КСВ ниже SWR-бара
|
||||
'const swrMarks=[1,2.5,5,10],swrLbls=["1","2.5","5","10"];' +
|
||||
'for(let i=0;i<swrMarks.length;i++){const x=swrX(swrMarks[i]),isW=swrMarks[i]>=SWR_HIGH;' +
|
||||
'mctx.strokeStyle=isW?"#90A070":"#708070";mctx.lineWidth=1;mctx.beginPath();mctx.moveTo(x,SY2+1);mctx.lineTo(x,SY2+TICK_LONG);mctx.stroke();' +
|
||||
'const t=swrLbls[i],tw=mctx.measureText(t).width;' +
|
||||
'mctx.fillStyle=isW?(swrHigh?"#CC0000":"#90D090"):"#E0E0E0";' +
|
||||
'mctx.fillText(t,Math.round(x-tw/2),SY2+TICK_LONG+11);}' +
|
||||
// надписи в левой зоне
|
||||
'mctx.font="bold 12px Courier New";' +
|
||||
'mctx.fillStyle="#DCD896";mctx.fillText(lblPwr,lblZoneL+4,PY1-12-2);' +
|
||||
'mctx.fillStyle=swrHigh?"#CC0000":"#F0F0F0";mctx.fillText(lblSWR,lblZoneL+4,SY2+14);' +
|
||||
// "SWR High" рядом с мощностью
|
||||
'if(swrHigh){const twP=mctx.measureText(lblPwr).width;' +
|
||||
'mctx.font="bold 8px Courier New";mctx.fillStyle="#CC0000";' +
|
||||
'mctx.fillText("SWR High",lblZoneL+4+twP+2,PY1-8);}}' +
|
||||
|
||||
// pendingCmds: команды в пути — не перезаписываем эти поля из server state
|
||||
'var pendingCmds={};' +
|
||||
'function pend(field){pendingCmds[field]=Date.now()+600;}' +
|
||||
|
||||
Reference in New Issue
Block a user