diff --git a/MainForm.pas b/MainForm.pas index ac7a4b8..6a67da3 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -2804,7 +2804,8 @@ begin FCenterFreq, FFilter, FVfoB, FActiveVfo, FTransmitting, TrkDrive.Position, - FAtten, FTuning, FDisplayDuplex); + FAtten, FTuning, FDisplayDuplex, + FLastFwdW, FLastSWR, FPAMaxPower); end; // =========================================================================== diff --git a/WebPageHtml.pas b/WebPageHtml.pas index 92d96df..25105be 100644 --- a/WebPageHtml.pas +++ b/WebPageHtml.pas @@ -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=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;}' + diff --git a/WebServer.pas b/WebServer.pas index 5c31186..5f9fe1b 100644 --- a/WebServer.pas +++ b/WebServer.pas @@ -247,6 +247,10 @@ type FCurrentXvtr: Integer; // -1 = HF, иначе индекс активного XVTR FOnXvtrBand: TWebCmdXvtrBand; + FFwdW: Double; + FSWR: Double; + FPAMaxPower: Double; + FWebClientActive: Boolean; // ── Внутренние методы ── @@ -290,7 +294,8 @@ type CenterHz: Double; FilterIdx: Integer; VfoB: Double; ActiveVfo: Integer; Transmitting: Boolean; DriveLevel: Integer; - AttnIdx: Integer; Tuning: Boolean; Duplex: Boolean); + AttnIdx: Integer; Tuning: Boolean; Duplex: Boolean; + FwdW, SWRV, PAMaxPower: Double); property WebClientActive: Boolean read FWebClientActive; property FreqMhzDigits: Integer read FFreqMhzDigits write FFreqMhzDigits; @@ -419,8 +424,11 @@ begin FAGCTop := 90; FSpanHz := 192000; FVolume := 70; - FSMeter := -120; - FBandIdx := 5; + FSMeter := -120; + FFwdW := 0; + FSWR := 1.0; + FPAMaxPower := 100.0; + FBandIdx := 5; FCurrentXvtr := -1; FFreqMhzDigits := 3; SetLength(FXvtrBands, 0); @@ -1236,6 +1244,7 @@ begin '"running":%s,"mute":%s,"ctun":%s,' + '"nr_mode":%d,"nr":%s,"nb_mode":%d,"nb":%s,"snb":%s,"anf":%s,"connected":%s,' + '"transmitting":%s,"drive":%d,"attn_idx":%d,"tuning":%s,"dup":%s,' + + '"fwd_w":%.1f,"swr":%.2f,"pa_max_power":%.0f,' + '"xvtr_current":%d,"xvtr_bands":%s,"freq_mhz_digits":%d}', [FFreq, FVfoB, FActiveVfo, FMode, MODE_N[FMode mod 8], @@ -1260,6 +1269,7 @@ begin FAttnIdx, BoolToStr(FTuning, 'true', 'false'), BoolToStr(FDuplex, 'true', 'false'), + FFwdW, FSWR, FPAMaxPower, FCurrentXvtr, XvtrJson, FFreqMhzDigits ], FS); finally @@ -1327,7 +1337,8 @@ procedure TWebServer.PushSpectrum( CenterHz: Double; FilterIdx: Integer; VfoB: Double; ActiveVfo: Integer; Transmitting: Boolean; DriveLevel: Integer; - AttnIdx: Integer; Tuning: Boolean; Duplex: Boolean); + AttnIdx: Integer; Tuning: Boolean; Duplex: Boolean; + FwdW, SWRV, PAMaxPower: Double); var N, i: Integer; begin if not FRunning then Exit; @@ -1364,6 +1375,9 @@ begin FAttnIdx := AttnIdx; FTuning := Tuning; FDuplex := Duplex; + FFwdW := FwdW; + FSWR := SWRV; + FPAMaxPower := PAMaxPower; finally FStateLock.Leave; end;