mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
fix vfo on web
This commit is contained in:
+79
-6
@@ -476,6 +476,8 @@ type
|
|||||||
procedure WebOnNB(Mode: Integer);
|
procedure WebOnNB(Mode: Integer);
|
||||||
procedure WebOnSNB(On_: Boolean);
|
procedure WebOnSNB(On_: Boolean);
|
||||||
procedure WebOnANF(On_: Boolean);
|
procedure WebOnANF(On_: Boolean);
|
||||||
|
procedure WebOnFreqB(Hz: Double);
|
||||||
|
procedure WebOnActiveVfo(Idx: Integer);
|
||||||
// Synchronize-обёртки (выполняются в UI-потоке)
|
// Synchronize-обёртки (выполняются в UI-потоке)
|
||||||
procedure SyncWebFreq;
|
procedure SyncWebFreq;
|
||||||
procedure SyncWebMode;
|
procedure SyncWebMode;
|
||||||
@@ -494,6 +496,8 @@ type
|
|||||||
procedure SyncWebNB;
|
procedure SyncWebNB;
|
||||||
procedure SyncWebSNB;
|
procedure SyncWebSNB;
|
||||||
procedure SyncWebANF;
|
procedure SyncWebANF;
|
||||||
|
procedure SyncWebFreqB;
|
||||||
|
procedure SyncWebActiveVfo;
|
||||||
procedure BtnWfAGCClick(Sender: TObject);
|
procedure BtnWfAGCClick(Sender: TObject);
|
||||||
procedure BtnWfNFClick(Sender: TObject);
|
procedure BtnWfNFClick(Sender: TObject);
|
||||||
procedure BtnHidePanelClick(Sender: TObject);
|
procedure BtnHidePanelClick(Sender: TObject);
|
||||||
@@ -822,6 +826,8 @@ begin
|
|||||||
FWebServer.OnNB := WebOnNB;
|
FWebServer.OnNB := WebOnNB;
|
||||||
FWebServer.OnSNB := WebOnSNB;
|
FWebServer.OnSNB := WebOnSNB;
|
||||||
FWebServer.OnANF := WebOnANF;
|
FWebServer.OnANF := WebOnANF;
|
||||||
|
FWebServer.OnFreqB := WebOnFreqB;
|
||||||
|
FWebServer.OnActiveVfo := WebOnActiveVfo;
|
||||||
FWebServer.Start;
|
FWebServer.Start;
|
||||||
FSMeterPeak := -130;
|
FSMeterPeak := -130;
|
||||||
FSMeterMin := -130;
|
FSMeterMin := -130;
|
||||||
@@ -2906,7 +2912,8 @@ begin
|
|||||||
FRunning and FNetwork.Connected,
|
FRunning and FNetwork.Connected,
|
||||||
FRunning, FMuted, FCTun,
|
FRunning, FMuted, FCTun,
|
||||||
BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0,
|
BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0,
|
||||||
FCenterFreq, FFilter);
|
FCenterFreq, FFilter,
|
||||||
|
FVfoB, FActiveVfo);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -4357,15 +4364,30 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.SyncWebFreq;
|
procedure TMainForm.SyncWebFreq;
|
||||||
|
var BandIdx: Integer;
|
||||||
begin
|
begin
|
||||||
FVfoA := FWebSyncFreq;
|
// Веб шлёт "freq" для активного VFO
|
||||||
if not FCTun then FCenterFreq := FVfoA;
|
if FActiveVfo = 0 then
|
||||||
UpdateVfoDisplay;
|
ApplyVfoA(Round(FWebSyncFreq))
|
||||||
if FWDSPReady then
|
else
|
||||||
begin
|
begin
|
||||||
FDSPEngine.SetShift(FVfoA - FCenterFreq);
|
FVfoB := FWebSyncFreq;
|
||||||
|
FreqDispB.Frequency := Round(FVfoB);
|
||||||
|
FCenterFreq := FVfoB;
|
||||||
|
if FWDSPReady then FDSPEngine.SetShift(0.0);
|
||||||
if FRunning then
|
if FRunning then
|
||||||
FNetwork.SetRunAndFreq(True, FCenterFreq, FCenterFreq, FDriveLevel);
|
FNetwork.SetRunAndFreq(True, FCenterFreq, FCenterFreq, FDriveLevel);
|
||||||
|
BandIdx := FreqToBandIdx(FVfoB);
|
||||||
|
if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then
|
||||||
|
begin
|
||||||
|
StyleButton(BtnBand[FCurrentBand], False);
|
||||||
|
FCurrentBand := BandIdx;
|
||||||
|
StyleButton(BtnBand[FCurrentBand], True);
|
||||||
|
end;
|
||||||
|
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0;
|
||||||
|
DrawSpectrum; PbSpectrum.Invalidate;
|
||||||
|
if PbRuler <> nil then PbRuler.Invalidate;
|
||||||
|
UpdateVfoDisplay;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -4682,6 +4704,57 @@ begin
|
|||||||
BtnANFClick(BtnANF);
|
BtnANFClick(BtnANF);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// ── VFO-B freq (из веба) ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
procedure TMainForm.WebOnFreqB(Hz: Double);
|
||||||
|
begin
|
||||||
|
FWebSyncFreq := Hz;
|
||||||
|
FWebSyncM := SyncWebFreqB;
|
||||||
|
TThread.Synchronize(nil, FWebSyncM);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.SyncWebFreqB;
|
||||||
|
var BandIdx: Integer;
|
||||||
|
begin
|
||||||
|
FVfoB := FWebSyncFreq;
|
||||||
|
FreqDispB.Frequency := Round(FVfoB);
|
||||||
|
if FActiveVfo = 1 then
|
||||||
|
begin
|
||||||
|
FCenterFreq := FVfoB;
|
||||||
|
if not FCTun then
|
||||||
|
if FWDSPReady then FDSPEngine.SetShift(0.0);
|
||||||
|
if FRunning then
|
||||||
|
FNetwork.SetRunAndFreq(True, FCenterFreq, FCenterFreq, FDriveLevel);
|
||||||
|
BandIdx := FreqToBandIdx(FVfoB);
|
||||||
|
if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then
|
||||||
|
begin
|
||||||
|
StyleButton(BtnBand[FCurrentBand], False);
|
||||||
|
FCurrentBand := BandIdx;
|
||||||
|
StyleButton(BtnBand[FCurrentBand], True);
|
||||||
|
end;
|
||||||
|
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0;
|
||||||
|
DrawSpectrum; PbSpectrum.Invalidate;
|
||||||
|
if PbRuler <> nil then PbRuler.Invalidate;
|
||||||
|
end;
|
||||||
|
UpdateVfoDisplay;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// ── Активный VFO (из веба) ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
procedure TMainForm.WebOnActiveVfo(Idx: Integer);
|
||||||
|
begin
|
||||||
|
FWebSyncInt := Idx;
|
||||||
|
FWebSyncM := SyncWebActiveVfo;
|
||||||
|
TThread.Synchronize(nil, FWebSyncM);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.SyncWebActiveVfo;
|
||||||
|
begin
|
||||||
|
if (FWebSyncInt < 0) or (FWebSyncInt > 1) then Exit;
|
||||||
|
if FWebSyncInt = FActiveVfo then Exit;
|
||||||
|
ActivateVfo(FWebSyncInt);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TMainForm.TrkDriveChange(Sender: TObject);
|
procedure TMainForm.TrkDriveChange(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
FDriveLevel := Round(TrkDrive.Position / 100.0 * 255);
|
FDriveLevel := Round(TrkDrive.Position / 100.0 * 255);
|
||||||
|
|||||||
+71
-32
@@ -70,16 +70,16 @@ begin
|
|||||||
'.vfo-mid{flex:1;display:flex;align-items:center;justify-content:center;gap:5px}' +
|
'.vfo-mid{flex:1;display:flex;align-items:center;justify-content:center;gap:5px}' +
|
||||||
'.vbtn{width:38px;height:38px;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;cursor:pointer;border-radius:3px;color:#888;border:1px solid #444;background:linear-gradient(180deg,#2a2a2a,#1e1e1e);padding:0}' +
|
'.vbtn{width:38px;height:38px;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;cursor:pointer;border-radius:3px;color:#888;border:1px solid #444;background:linear-gradient(180deg,#2a2a2a,#1e1e1e);padding:0}' +
|
||||||
'.vbtn.active{color:#7fff7f;border-color:#3d7a3d;background:linear-gradient(180deg,#1e3e1e,#142814)}' +
|
'.vbtn.active{color:#7fff7f;border-color:#3d7a3d;background:linear-gradient(180deg,#1e3e1e,#142814)}' +
|
||||||
'.vfo-ro{font:400 44px "Orbitron","Consolas",monospace;letter-spacing:0.02em;color:#7fff7f;background:#0c0c0c;border:none;border-radius:0;padding:0 6px;cursor:pointer;user-select:none;height:62px;display:inline-flex;align-items:baseline;padding-top:10px;white-space:nowrap;overflow:hidden}' +
|
'.vfo-ro{font:400 44px "Orbitron","Consolas",monospace;letter-spacing:0.02em;color:#7fff7f;border:none;border-radius:0;padding:0 6px;cursor:pointer;user-select:none;height:62px;display:inline-flex;align-items:baseline;padding-top:10px;white-space:nowrap;overflow:hidden;background:transparent}' +
|
||||||
'.vfo-ro.dim{color:#2a4a2a;background:#1e1e1e;border:none}' +
|
'.vfo-ro.dim{color:#1e501e;background:transparent}' +
|
||||||
'.vfo-ro .dg{display:inline-block;min-width:0.55em;font-size:44px;line-height:1}' +
|
'.vfo-ro .dg{display:inline-block;min-width:0.55em;font-size:44px;line-height:1}' +
|
||||||
'.vfo-ro .dg.lead{color:#2a3a2a}.vfo-ro.dim .dg.lead{color:#1e281e}' +
|
'.vfo-ro .dg.lead{color:#2a3a2a}.vfo-ro.dim .dg.lead{color:#162016}' +
|
||||||
'.vfo-ro .sp{display:inline-block;color:#2a6030;min-width:0.3em;font-size:44px;line-height:1}' +
|
'.vfo-ro .sp{display:inline-block;color:#2a6030;min-width:0.3em;font-size:44px;line-height:1}' +
|
||||||
'.vfo-ro.dim .sp{color:#1a2a1a}' +
|
'.vfo-ro.dim .sp{color:#142014}' +
|
||||||
'.vfo-ro .dg.sel{background:rgba(127,255,127,.18);outline:1px solid rgba(127,255,127,.35);border-radius:2px}' +
|
'.vfo-ro .dg.sel{background:rgba(127,255,127,.18);outline:1px solid rgba(127,255,127,.35);border-radius:2px}' +
|
||||||
'.vfo-hz{display:inline-flex;align-items:baseline}' +
|
'.vfo-hz{display:inline-flex;align-items:baseline}' +
|
||||||
'.vfo-hz .dg{display:inline-block;font-size:22px;line-height:1;min-width:0.55em;font-weight:400}' +
|
'.vfo-hz .dg{display:inline-block;font-size:22px;line-height:1;min-width:0.55em;font-weight:400}' +
|
||||||
'.vfo-ro.dim .vfo-hz .dg{color:#2a4a2a}' +
|
'.vfo-ro.dim .vfo-hz .dg{color:#1e501e}' +
|
||||||
'.swap-g{display:flex;gap:3px}' +
|
'.swap-g{display:flex;gap:3px}' +
|
||||||
'.swap-g button{padding:0 7px;height:22px;font-size:10px}' +
|
'.swap-g button{padding:0 7px;height:22px;font-size:10px}' +
|
||||||
'.right-g{display:flex;align-items:center;gap:4px;margin-left:auto}' +
|
'.right-g{display:flex;align-items:center;gap:4px;margin-left:auto}' +
|
||||||
@@ -238,7 +238,7 @@ begin
|
|||||||
'return h;}' +
|
'return h;}' +
|
||||||
'function renderVfos(){q("vDispA").innerHTML=fmtVfo(vfoA,stA);q("vDispB").innerHTML=fmtVfo(vfoB,stB);}' +
|
'function renderVfos(){q("vDispA").innerHTML=fmtVfo(vfoA,stA);q("vDispB").innerHTML=fmtVfo(vfoB,stB);}' +
|
||||||
|
|
||||||
'function bindVfo(dispId,getHz,setHz,getSt,setSt,getMem,setMem){' +
|
'function bindVfo(dispId,getHz,setHz,getSt,setSt,getMem,setMem,onSend){' +
|
||||||
'const el=q(dispId);' +
|
'const el=q(dispId);' +
|
||||||
'el.addEventListener("mousemove",e=>{const t=e.target,s=Number(t?.dataset?.s||0);if(s>0){setSt(s);setMem(s);renderVfos();}});' +
|
'el.addEventListener("mousemove",e=>{const t=e.target,s=Number(t?.dataset?.s||0);if(s>0){setSt(s);setMem(s);renderVfos();}});' +
|
||||||
'el.addEventListener("click",e=>{const t=e.target,s=Number(t?.dataset?.s||0);if(s>0){setSt(s);setMem(s);}});' +
|
'el.addEventListener("click",e=>{const t=e.target,s=Number(t?.dataset?.s||0);if(s>0){setSt(s);setMem(s);}});' +
|
||||||
@@ -246,21 +246,41 @@ begin
|
|||||||
'el.addEventListener("wheel",e=>{e.preventDefault();e.stopPropagation();audioKick();' +
|
'el.addEventListener("wheel",e=>{e.preventDefault();e.stopPropagation();audioKick();' +
|
||||||
'const dir=e.deltaY<0?1:-1,st=getSt()||getMem()||100;' +
|
'const dir=e.deltaY<0?1:-1,st=getSt()||getMem()||100;' +
|
||||||
'const nf=cl(Math.round(getHz())+st*dir,30000,60000000);' +
|
'const nf=cl(Math.round(getHz())+st*dir,30000,60000000);' +
|
||||||
'setHz(nf);renderVfos();pend("freq");ws2({cmd:"freq",hz:nf});},{passive:false});' +
|
'setHz(nf);onSend(nf);},{passive:false});' +
|
||||||
'el.addEventListener("dblclick",()=>{' +
|
'el.addEventListener("dblclick",()=>{' +
|
||||||
'const s=prompt("Set Hz",String(Math.round(getHz())));if(!s)return;' +
|
'const s=prompt("Set Hz",String(Math.round(getHz())));if(!s)return;' +
|
||||||
'const f=parseInt(s.replace(/[^0-9]/g,""),10);if(!isFinite(f))return;' +
|
'const f=parseInt(s.replace(/[^0-9]/g,""),10);if(!isFinite(f))return;' +
|
||||||
'const nf=cl(f,30000,60000000);setHz(nf);renderVfos();ws2({cmd:"freq",hz:nf});' +
|
'const nf=cl(f,30000,60000000);setHz(nf);renderVfos();onSend(nf);' +
|
||||||
'});}' +
|
'});}' +
|
||||||
'bindVfo("vDispA",()=>vfoA,v=>{vfoA=v;},()=>stA,v=>{stA=v;},()=>stAmem,v=>{stAmem=v;});' +
|
'bindVfo("vDispA",()=>vfoA,v=>{vfoA=v;},()=>stA,v=>{stA=v;},()=>stAmem,v=>{stAmem=v;},' +
|
||||||
'bindVfo("vDispB",()=>vfoB,v=>{vfoB=v;},()=>stB,v=>{stB=v;},()=>stBmem,v=>{stBmem=v;});' +
|
'function(nf){renderVfos();pend("freq");if(activeVfo==="A")ws2({cmd:"freq",hz:nf});else ws2({cmd:"freq_b",hz:nf});});' +
|
||||||
|
'bindVfo("vDispB",()=>vfoB,v=>{vfoB=v;},()=>stB,v=>{stB=v;},()=>stBmem,v=>{stBmem=v;},' +
|
||||||
|
'function(nf){renderVfos();pend("freq");if(activeVfo==="B")ws2({cmd:"freq",hz:nf});else ws2({cmd:"freq_b",hz:nf});});' +
|
||||||
|
|
||||||
'function selVfo(w){activeVfo=w;q("vBtnA").classList.toggle("active",w==="A");q("vBtnB").classList.toggle("active",w==="B");q("vDispA").classList.toggle("dim",w!="A");q("vDispB").classList.toggle("dim",w!="B");ws2({cmd:"freq",hz:Math.round(w==="A"?vfoA:vfoB)});}' +
|
'function selVfo(w){' +
|
||||||
'q("vBtnA").onclick=()=>selVfo("A");' +
|
'activeVfo=w;' +
|
||||||
'q("vBtnB").onclick=()=>selVfo("B");' +
|
'q("vBtnA").classList.toggle("active",w==="A");' +
|
||||||
'q("btnBA").onclick=()=>{vfoA=vfoB;renderVfos();ws2({cmd:"freq",hz:Math.round(vfoA)});};' +
|
'q("vBtnB").classList.toggle("active",w==="B");' +
|
||||||
'q("btnAB").onclick=()=>{vfoB=vfoA;renderVfos();};' +
|
'q("vDispA").classList.toggle("dim",w!=="A");' +
|
||||||
'q("btnSwap").onclick=()=>{const t=vfoA;vfoA=vfoB;vfoB=t;renderVfos();ws2({cmd:"freq",hz:Math.round(activeVfo==="A"?vfoA:vfoB)});};' +
|
'q("vDispB").classList.toggle("dim",w!=="B");' +
|
||||||
|
// Notify server: switch active VFO (server calls ActivateVfo which retunes DDC)
|
||||||
|
'ws2({cmd:"set_active_vfo",idx:w==="A"?0:1});' +
|
||||||
|
'}' +
|
||||||
|
'q("vBtnA").onclick=()=>{if(activeVfo!=="A")selVfo("A");};' +
|
||||||
|
'q("vBtnB").onclick=()=>{if(activeVfo!=="B")selVfo("B");};' +
|
||||||
|
// B=A: copy A freq into B
|
||||||
|
'q("btnBA").onclick=()=>{vfoB=vfoA;renderVfos();ws2({cmd:"freq_b",hz:Math.round(vfoB)});};' +
|
||||||
|
// A=B: copy B freq into A
|
||||||
|
'q("btnAB").onclick=()=>{vfoA=vfoB;renderVfos();' +
|
||||||
|
'if(activeVfo==="A")ws2({cmd:"freq",hz:Math.round(vfoA)});' +
|
||||||
|
'else ws2({cmd:"freq_b",hz:Math.round(vfoA)});' +
|
||||||
|
// Actually A=B means set vfoA=vfoB, so send freq_a regardless of active
|
||||||
|
'};' +
|
||||||
|
// SWAP
|
||||||
|
'q("btnSwap").onclick=()=>{const t=vfoA;vfoA=vfoB;vfoB=t;renderVfos();' +
|
||||||
|
'ws2({cmd:"freq",hz:Math.round(activeVfo==="A"?vfoA:vfoB)});' +
|
||||||
|
'ws2({cmd:"freq_b",hz:Math.round(activeVfo==="A"?vfoB:vfoA)});' +
|
||||||
|
'};' +
|
||||||
|
|
||||||
// SPAN кнопки: только 48K..1536K
|
// SPAN кнопки: только 48K..1536K
|
||||||
'const SPANS=[48000,96000,192000,384000,768000,1536000];' +
|
'const SPANS=[48000,96000,192000,384000,768000,1536000];' +
|
||||||
@@ -330,7 +350,7 @@ begin
|
|||||||
'function isPending(field){return !!(pendingCmds[field]&&pendingCmds[field]>Date.now());}' +
|
'function isPending(field){return !!(pendingCmds[field]&&pendingCmds[field]>Date.now());}' +
|
||||||
|
|
||||||
// Маппинг: server JSON field → pend key
|
// Маппинг: server JSON field → pend key
|
||||||
'var PEND_MAP={vfo_a_hz:"freq",nr:"nr",nr_mode:"nr",nb:"nb",nb_mode:"nb",snb:"snb",anf:"anf",running:"run",mode:"mode",agc_mode:"agc"};' +
|
'var PEND_MAP={vfo_a_hz:"freq",vfo_b_hz:"freq",nr:"nr",nr_mode:"nr",nb:"nb",nb_mode:"nb",snb:"snb",anf:"anf",running:"run",mode:"mode",agc_mode:"agc"};' +
|
||||||
|
|
||||||
'function applyState(msg){' +
|
'function applyState(msg){' +
|
||||||
// Мержим только поля которые не pending
|
// Мержим только поля которые не pending
|
||||||
@@ -338,8 +358,21 @@ begin
|
|||||||
'var pk=PEND_MAP[k];' +
|
'var pk=PEND_MAP[k];' +
|
||||||
'if(!pk||!isPending(pk))cur[k]=msg[k];' +
|
'if(!pk||!isPending(pk))cur[k]=msg[k];' +
|
||||||
'});' +
|
'});' +
|
||||||
// VFO
|
// VFO A
|
||||||
'if(!isPending("freq")){vfoA=cur.vfo_a_hz||vfoA;renderVfos();}' +
|
'if(!isPending("freq")){vfoA=cur.vfo_a_hz||vfoA;}' +
|
||||||
|
// VFO B (always sync - no separate pending for B)
|
||||||
|
'if(msg.vfo_b_hz!=null)vfoB=msg.vfo_b_hz;' +
|
||||||
|
// Active VFO from server (authoritative)
|
||||||
|
'if(msg.active_vfo!=null&&!isPending("freq")){' +
|
||||||
|
'const sv=msg.active_vfo===0?"A":"B";' +
|
||||||
|
'if(sv!==activeVfo){' +
|
||||||
|
'activeVfo=sv;' +
|
||||||
|
'q("vBtnA").classList.toggle("active",activeVfo==="A");' +
|
||||||
|
'q("vBtnB").classList.toggle("active",activeVfo==="B");' +
|
||||||
|
'q("vDispA").classList.toggle("dim",activeVfo!=="A");' +
|
||||||
|
'q("vDispB").classList.toggle("dim",activeVfo!=="B");' +
|
||||||
|
'}}' +
|
||||||
|
'renderVfos();' +
|
||||||
'const as=q("agcSel");if(as&&cur.agc_mode!=null&&!isPending("agc"))as.value=cur.agc_mode;' +
|
'const as=q("agcSel");if(as&&cur.agc_mode!=null&&!isPending("agc"))as.value=cur.agc_mode;' +
|
||||||
'const vs=q("volSl");if(vs&&!vs.matches(":active")){vs.value=cur.volume||70;var vv=q("volVal");if(vv)vv.textContent=cur.volume||70;}' +
|
'const vs=q("volSl");if(vs&&!vs.matches(":active")){vs.value=cur.volume||70;var vv=q("volVal");if(vv)vv.textContent=cur.volume||70;}' +
|
||||||
'const rs=q("rfSl");if(rs&&!rs.matches(":active")){rs.value=cur.agc_top||90;var rv=q("rfVal");if(rv)rv.textContent=(cur.agc_top||90)+"dB";}' +
|
'const rs=q("rfSl");if(rs&&!rs.matches(":active")){rs.value=cur.agc_top||90;var rv=q("rfVal");if(rv)rv.textContent=(cur.agc_top||90)+"dB";}' +
|
||||||
@@ -348,12 +381,11 @@ begin
|
|||||||
'if(!isPending("snb"))updSnbBtn();' +
|
'if(!isPending("snb"))updSnbBtn();' +
|
||||||
'if(!isPending("anf"))updAnfBtn();' +
|
'if(!isPending("anf"))updAnfBtn();' +
|
||||||
'if(!isPending("run"))updRunBtn(!!cur.running);' +
|
'if(!isPending("run"))updRunBtn(!!cur.running);' +
|
||||||
// syncModGrp syncs dropdown AND calls updModTglLabel internally
|
|
||||||
'if(!isPending("mode")){syncModGrp(cur.mode||0);}' +
|
'if(!isPending("mode")){syncModGrp(cur.mode||0);}' +
|
||||||
'updBand(cur.vfo_a_hz||0);updSpan(cur.span_hz||192000);' +
|
'updBand(activeVfo==="A"?(cur.vfo_a_hz||0):(vfoB||0));updSpan(cur.span_hz||192000);' +
|
||||||
'paintSM(Number(cur.smeter_dbm||-130));' +
|
'paintSM(Number(cur.smeter_dbm||-130));' +
|
||||||
'const mn=cur.mode_name||(MODE_N[cur.mode||0]||"?");' +
|
'const mn=cur.mode_name||(MODE_N[cur.mode||0]||"?");' +
|
||||||
'stEl.textContent=(cur.connected?"OK":"OFF")+" | "+(cur.running?"RUN":"STOP")+" | "+mn+" | "+((vfoA/1e6).toFixed(3))+" MHz";}' +
|
'stEl.textContent=(cur.connected?"OK":"OFF")+" | "+(cur.running?"RUN":"STOP")+" | "+mn+" | "+((activeVfoHz()/1e6).toFixed(3))+" MHz";}' +
|
||||||
|
|
||||||
'function pal(db){const v=Math.round(cl((db-wfLo)/Math.max(1,wfHi-wfLo),0,1)*255);const c=PAL[v]|0;return[(c>>16)&255,(c>>8)&255,c&255];}' +
|
'function pal(db){const v=Math.round(cl((db-wfLo)/Math.max(1,wfHi-wfLo),0,1)*255);const c=PAL[v]|0;return[(c>>16)&255,(c>>8)&255,c&255];}' +
|
||||||
'function fltLH(){const bw=cur.filter_bw||2700;const m=cur.mode|0;if(m===0)return[-bw,-100];if(m===1)return[100,bw];return[-bw/2,bw/2];}' +
|
'function fltLH(){const bw=cur.filter_bw||2700;const m=cur.mode|0;if(m===0)return[-bw,-100];if(m===1)return[100,bw];return[-bw/2,bw/2];}' +
|
||||||
@@ -366,20 +398,20 @@ begin
|
|||||||
'function drawRuler(){const w=rulerCv.clientWidth,h=rulerCv.clientHeight;rctx.fillStyle="#0c1210";rctx.fillRect(0,0,w,h);' +
|
'function drawRuler(){const w=rulerCv.clientWidth,h=rulerCv.clientHeight;rctx.fillStyle="#0c1210";rctx.fillRect(0,0,w,h);' +
|
||||||
'const st=(cur.center_hz||0)-(cur.span_hz||0)/2;' +
|
'const st=(cur.center_hz||0)-(cur.span_hz||0)/2;' +
|
||||||
'for(let i=0;i<=8;i++){const x=i*w/8,f=st+i*(cur.span_hz||0)/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);}' +
|
'for(let i=0;i<=8;i++){const x=i*w/8,f=st+i*(cur.span_hz||0)/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 vx=hzToX(vfoA,w);rctx.strokeStyle="#60d070";rctx.lineWidth=1;rctx.beginPath();rctx.moveTo(vx+.5,0);rctx.lineTo(vx+.5,h);rctx.stroke();}' +
|
'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);' +
|
'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();}' +
|
'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(!bins?.length){drawMk(sctx,w,h);return;}' +
|
'if(!bins?.length){drawMk(sctx,w,h);return;}' +
|
||||||
'if(specSm.length!==bins.length)specSm=new Array(bins.length).fill(-130);' +
|
'if(specSm.length!==bins.length)specSm=new Array(bins.length).fill(-130);' +
|
||||||
'const[fl,fh]=fltLH(),x1=hzToX(vfoA+fl,w),x2=hzToX(vfoA+fh,w);' +
|
'const avf=activeVfoHz();const[fl,fh]=fltLH(),x1=hzToX(avf+fl,w),x2=hzToX(avf+fh,w);' +
|
||||||
'sctx.fillStyle="rgba(80,150,255,.12)";sctx.fillRect(Math.min(x1,x2),0,Math.abs(x2-x1),h);' +
|
'sctx.fillStyle="rgba(80,150,255,.12)";sctx.fillRect(Math.min(x1,x2),0,Math.abs(x2-x1),h);' +
|
||||||
'sctx.beginPath();for(let x=0;x<w;x++){const i=Math.floor(x*(bins.length-1)/Math.max(1,w-1));specSm[i]=specSm[i]*.70+bins[i]*.30;const y=cl(((-specSm[i]-20)/120)*h,0,h-1);x===0?sctx.moveTo(0,y):sctx.lineTo(x,y);}' +
|
'sctx.beginPath();for(let x=0;x<w;x++){const i=Math.floor(x*(bins.length-1)/Math.max(1,w-1));specSm[i]=specSm[i]*.70+bins[i]*.30;const y=cl(((-specSm[i]-20)/120)*h,0,h-1);x===0?sctx.moveTo(0,y):sctx.lineTo(x,y);}' +
|
||||||
'const g=sctx.createLinearGradient(0,0,0,h);g.addColorStop(0,"rgba(60,230,60,.2)");g.addColorStop(1,"rgba(60,230,60,.01)");' +
|
'const g=sctx.createLinearGradient(0,0,0,h);g.addColorStop(0,"rgba(60,230,60,.2)");g.addColorStop(1,"rgba(60,230,60,.01)");' +
|
||||||
'sctx.lineTo(w,h);sctx.lineTo(0,h);sctx.closePath();sctx.fillStyle=g;sctx.fill();' +
|
'sctx.lineTo(w,h);sctx.lineTo(0,h);sctx.closePath();sctx.fillStyle=g;sctx.fill();' +
|
||||||
'sctx.beginPath();for(let x=0;x<w;x++){const i=Math.floor(x*(bins.length-1)/Math.max(1,w-1));const y=cl(((-specSm[i]-20)/120)*h,0,h-1);x===0?sctx.moveTo(0,y):sctx.lineTo(x,y);}' +
|
'sctx.beginPath();for(let x=0;x<w;x++){const i=Math.floor(x*(bins.length-1)/Math.max(1,w-1));const y=cl(((-specSm[i]-20)/120)*h,0,h-1);x===0?sctx.moveTo(0,y):sctx.lineTo(x,y);}' +
|
||||||
'sctx.strokeStyle="#3ddd3d";sctx.lineWidth=1.2;sctx.stroke();' +
|
'sctx.strokeStyle="#3ddd3d";sctx.lineWidth=1.2;sctx.stroke();' +
|
||||||
'const vx=hzToX(vfoA,w);sctx.strokeStyle="#60ff80";sctx.lineWidth=1;sctx.beginPath();sctx.moveTo(vx,0);sctx.lineTo(vx,h);sctx.stroke();drawMk(sctx,w,h);}' +
|
'const vx=hzToX(avf,w);sctx.strokeStyle="#60ff80";sctx.lineWidth=1;sctx.beginPath();sctx.moveTo(vx,0);sctx.lineTo(vx,h);sctx.stroke();drawMk(sctx,w,h);}' +
|
||||||
|
|
||||||
'function drawWf(bins){const w=wfCv.clientWidth,h=wfCv.clientHeight;' +
|
'function drawWf(bins){const w=wfCv.clientWidth,h=wfCv.clientHeight;' +
|
||||||
'if(!bins?.length){drawMk(wctx,w,h);return;}' +
|
'if(!bins?.length){drawMk(wctx,w,h);return;}' +
|
||||||
@@ -397,22 +429,29 @@ begin
|
|||||||
'wfBCtx.drawImage(wfBuf,0,0,w,h-1,0,1,w,h-1);const row=wfBCtx.createImageData(w,1);' +
|
'wfBCtx.drawImage(wfBuf,0,0,w,h-1,0,1,w,h-1);const row=wfBCtx.createImageData(w,1);' +
|
||||||
'for(let x=0;x<w;x++){const i=Math.floor(x*(wfAvg.length-1)/Math.max(1,w-1));const[r,g,b]=pal(wfAvg[i]);const o=x*4;row.data[o]=r;row.data[o+1]=g;row.data[o+2]=b;row.data[o+3]=255;}' +
|
'for(let x=0;x<w;x++){const i=Math.floor(x*(wfAvg.length-1)/Math.max(1,w-1));const[r,g,b]=pal(wfAvg[i]);const o=x*4;row.data[o]=r;row.data[o+1]=g;row.data[o+2]=b;row.data[o+3]=255;}' +
|
||||||
'wfBCtx.putImageData(row,0,0);wctx.drawImage(wfBuf,0,0,w,h);' +
|
'wfBCtx.putImageData(row,0,0);wctx.drawImage(wfBuf,0,0,w,h);' +
|
||||||
'const[fl,fh]=fltLH(),x1=hzToX(vfoA+fl,w),x2=hzToX(vfoA+fh,w);' +
|
'const avf2=activeVfoHz();const[fl2,fh2]=fltLH(),x1w=hzToX(avf2+fl2,w),x2w=hzToX(avf2+fh2,w);' +
|
||||||
'wctx.fillStyle="rgba(220,230,230,.10)";wctx.fillRect(Math.min(x1,x2),0,Math.abs(x2-x1),h);' +
|
'wctx.fillStyle="rgba(220,230,230,.10)";wctx.fillRect(Math.min(x1w,x2w),0,Math.abs(x2w-x1w),h);' +
|
||||||
'const vx=hzToX(vfoA,w);wctx.strokeStyle="#60ff80";wctx.lineWidth=1;wctx.beginPath();wctx.moveTo(vx,0);wctx.lineTo(vx,h);wctx.stroke();drawMk(wctx,w,h);}' +
|
'const vxw=hzToX(avf2,w);wctx.strokeStyle="#60ff80";wctx.lineWidth=1;wctx.beginPath();wctx.moveTo(vxw,0);wctx.lineTo(vxw,h);wctx.stroke();drawMk(wctx,w,h);}' +
|
||||||
|
|
||||||
'function rcv(cv){const dpr=Math.max(1,devicePixelRatio||1),w=cv.clientWidth|0,h=cv.clientHeight|0;if(cv.width!==(w*dpr|0)||cv.height!==(h*dpr|0)){cv.width=w*dpr;cv.height=h*dpr;cv.getContext("2d").setTransform(dpr,0,0,dpr,0,0);}}' +
|
'function rcv(cv){const dpr=Math.max(1,devicePixelRatio||1),w=cv.clientWidth|0,h=cv.clientHeight|0;if(cv.width!==(w*dpr|0)||cv.height!==(h*dpr|0)){cv.width=w*dpr;cv.height=h*dpr;cv.getContext("2d").setTransform(dpr,0,0,dpr,0,0);}}' +
|
||||||
'function resize(){rcv(specCv);rcv(wfCv);rcv(rulerCv);const sw=smCv.clientWidth|0,sh=smCv.clientHeight|0;if(smCv.width!==sw||smCv.height!==sh){smCv.width=sw;smCv.height=sh;}if(wfBuf.width!==wfCv.clientWidth||wfBuf.height!==wfCv.clientHeight){wfBuf.width=wfCv.clientWidth;wfBuf.height=wfCv.clientHeight;wfBCtx.fillStyle="#000";wfBCtx.fillRect(0,0,wfBuf.width,wfBuf.height);}}' +
|
'function resize(){rcv(specCv);rcv(wfCv);rcv(rulerCv);const sw=smCv.clientWidth|0,sh=smCv.clientHeight|0;if(smCv.width!==sw||smCv.height!==sh){smCv.width=sw;smCv.height=sh;}if(wfBuf.width!==wfCv.clientWidth||wfBuf.height!==wfCv.clientHeight){wfBuf.width=wfCv.clientWidth;wfBuf.height=wfCv.clientHeight;wfBCtx.fillStyle="#000";wfBCtx.fillRect(0,0,wfBuf.width,wfBuf.height);}}' +
|
||||||
|
|
||||||
'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;}' +
|
'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;}' +
|
||||||
'function doWheel(e){e.preventDefault();e.stopPropagation();audioKick();const dir=e.deltaY<0?1:-1,st=tuneStep(e),base=Math.floor(vfoA/st)*st;vfoA=cl(dir>0?base+st:(Math.round(vfoA)===base?base-st:base),30000,60000000);cur.vfo_a_hz=vfoA;renderVfos();pend("freq");ws2({cmd:"freq",hz:vfoA});}' +
|
'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 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),30000,60000000);' +
|
||||||
|
'setActiveVfoHz(nf);' +
|
||||||
|
'if(activeVfo==="A"){cur.vfo_a_hz=nf;}else{cur.vfo_b_hz=nf;}' +
|
||||||
|
'renderVfos();pend("freq");ws2({cmd:"freq",hz:nf});}' +
|
||||||
'function bindCv(cv){let wT=0;const wh=e=>{const n=performance.now();if(n-wT<20)return;wT=n;wTarget=cv.id;doWheel(e);};' +
|
'function bindCv(cv){let wT=0;const wh=e=>{const n=performance.now();if(n-wT<20)return;wT=n;wTarget=cv.id;doWheel(e);};' +
|
||||||
'cv.addEventListener("contextmenu",e=>e.preventDefault());' +
|
'cv.addEventListener("contextmenu",e=>e.preventDefault());' +
|
||||||
'cv.addEventListener("mouseenter",()=>{wTarget=cv.id;});' +
|
'cv.addEventListener("mouseenter",()=>{wTarget=cv.id;});' +
|
||||||
'cv.addEventListener("wheel",wh,{passive:false,capture:true});' +
|
'cv.addEventListener("wheel",wh,{passive:false,capture:true});' +
|
||||||
'cv.addEventListener("mousedown",e=>{audioKick();wTarget=cv.id;if(e.button===2){markerOn=Math.abs(markerX-e.offsetX)>=8||!markerOn;markerX=e.offsetX;return;}if(e.button!==0)return;dragging=true;dragX0=e.offsetX;dragCen0=cur.center_hz||0;dragVfo0=vfoA;dragMs=0;});' +
|
'cv.addEventListener("mousedown",e=>{audioKick();wTarget=cv.id;if(e.button===2){markerOn=Math.abs(markerX-e.offsetX)>=8||!markerOn;markerX=e.offsetX;return;}if(e.button!==0)return;dragging=true;dragX0=e.offsetX;dragCen0=cur.center_hz||0;dragVfo0=activeVfoHz();dragMs=0;});' +
|
||||||
'cv.addEventListener("mousemove",e=>{wTarget=cv.id;hoverX=e.offsetX;if(markerOn)markerX=e.offsetX;if(!dragging)return;const n=Date.now();if(n-dragMs<35)return;dragMs=n;const dHz=(e.offsetX-dragX0)/Math.max(1,cv.clientWidth)*(cur.span_hz||0);if(cur.ctun)ws2({cmd:"freq",hz:Math.round(dragCen0-dHz)});else{vfoA=cl(Math.round(dragVfo0-dHz),30000,60000000);cur.vfo_a_hz=vfoA;renderVfos();pend("freq");ws2({cmd:"freq",hz:vfoA});}});' +
|
'cv.addEventListener("mousemove",e=>{wTarget=cv.id;hoverX=e.offsetX;if(markerOn)markerX=e.offsetX;if(!dragging)return;const n=Date.now();if(n-dragMs<35)return;dragMs=n;const dHz=(e.offsetX-dragX0)/Math.max(1,cv.clientWidth)*(cur.span_hz||0);if(cur.ctun)ws2({cmd:"freq",hz:Math.round(dragCen0-dHz)});else{const nf=cl(Math.round(dragVfo0-dHz),30000,60000000);setActiveVfoHz(nf);if(activeVfo==="A")cur.vfo_a_hz=nf;else cur.vfo_b_hz=nf;renderVfos();pend("freq");ws2({cmd:"freq",hz:nf});}});' +
|
||||||
'cv.addEventListener("mouseup",e=>{if(e.button===2)return;if(Math.abs(e.offsetX-dragX0)<4){vfoA=cl(Math.round(xToHz(e.offsetX,cv.clientWidth)/100)*100,30000,60000000);cur.vfo_a_hz=vfoA;renderVfos();pend("freq");ws2({cmd:"freq",hz:vfoA});}dragging=false;});' +
|
'cv.addEventListener("mouseup",e=>{if(e.button===2)return;if(Math.abs(e.offsetX-dragX0)<4){const nf=cl(Math.round(xToHz(e.offsetX,cv.clientWidth)/100)*100,30000,60000000);setActiveVfoHz(nf);if(activeVfo==="A")cur.vfo_a_hz=nf;else cur.vfo_b_hz=nf;renderVfos();pend("freq");ws2({cmd:"freq",hz:nf});}dragging=false;});' +
|
||||||
'cv.addEventListener("mouseleave",()=>{if(!markerOn)hoverX=-1;wTarget="";dragging=false;});}' +
|
'cv.addEventListener("mouseleave",()=>{if(!markerOn)hoverX=-1;wTarget="";dragging=false;});}' +
|
||||||
'bindCv(specCv);bindCv(wfCv);' +
|
'bindCv(specCv);bindCv(wfCv);' +
|
||||||
'window.addEventListener("wheel",e=>{if(e.target?.closest?.(".vfo-ro"))return;if(!wTarget){if(e.target===specCv)wTarget="spec";else if(e.target===wfCv)wTarget="wf";}if(wTarget)doWheel(e);},{passive:false,capture:true});' +
|
'window.addEventListener("wheel",e=>{if(e.target?.closest?.(".vfo-ro"))return;if(!wTarget){if(e.target===specCv)wTarget="spec";else if(e.target===wfCv)wTarget="wf";}if(wTarget)doWheel(e);},{passive:false,capture:true});' +
|
||||||
|
|||||||
+36
-9
@@ -119,8 +119,10 @@ type
|
|||||||
TWebCmdCtun = procedure(On_: Boolean) of object;
|
TWebCmdCtun = procedure(On_: Boolean) of object;
|
||||||
TWebCmdNRMode = procedure(Mode: Integer) of object;
|
TWebCmdNRMode = procedure(Mode: Integer) of object;
|
||||||
TWebCmdNBMode = procedure(Mode: Integer) of object;
|
TWebCmdNBMode = procedure(Mode: Integer) of object;
|
||||||
TWebCmdSNB = procedure(On_: Boolean) of object;
|
TWebCmdSNB = procedure(On_: Boolean) of object;
|
||||||
TWebCmdANF = procedure(On_: Boolean) of object;
|
TWebCmdANF = procedure(On_: Boolean) of object;
|
||||||
|
TWebCmdFreqB = procedure(Hz: Double) of object;
|
||||||
|
TWebCmdActiveVfo = procedure(Idx: Integer) of object;
|
||||||
|
|
||||||
// ── Главный класс сервера ─────────────────────────────────────────────────
|
// ── Главный класс сервера ─────────────────────────────────────────────────
|
||||||
TWebServer = class
|
TWebServer = class
|
||||||
@@ -176,6 +178,8 @@ type
|
|||||||
FANF: Boolean;
|
FANF: Boolean;
|
||||||
FCenterHz: Double;
|
FCenterHz: Double;
|
||||||
FFilterIdx: Integer;
|
FFilterIdx: Integer;
|
||||||
|
FVfoB: Double;
|
||||||
|
FActiveVfo: Integer; // 0=A, 1=B
|
||||||
FStateLock: TCriticalSection;
|
FStateLock: TCriticalSection;
|
||||||
|
|
||||||
// ── Callbacks ──
|
// ── Callbacks ──
|
||||||
@@ -196,6 +200,8 @@ type
|
|||||||
FOnNB: TWebCmdNBMode;
|
FOnNB: TWebCmdNBMode;
|
||||||
FOnSNB: TWebCmdSNB;
|
FOnSNB: TWebCmdSNB;
|
||||||
FOnANF: TWebCmdANF;
|
FOnANF: TWebCmdANF;
|
||||||
|
FOnFreqB: TWebCmdFreqB;
|
||||||
|
FOnActiveVfo: TWebCmdActiveVfo;
|
||||||
|
|
||||||
FWebClientActive: Boolean;
|
FWebClientActive: Boolean;
|
||||||
|
|
||||||
@@ -237,7 +243,8 @@ type
|
|||||||
TrxConnected: Boolean;
|
TrxConnected: Boolean;
|
||||||
TrxRunning, Muted, Ctun: Boolean;
|
TrxRunning, Muted, Ctun: Boolean;
|
||||||
NRMode, NBMode: Integer; SNB, ANF: Boolean;
|
NRMode, NBMode: Integer; SNB, ANF: Boolean;
|
||||||
CenterHz: Double; FilterIdx: Integer);
|
CenterHz: Double; FilterIdx: Integer;
|
||||||
|
VfoB: Double; ActiveVfo: Integer);
|
||||||
|
|
||||||
property WebClientActive: Boolean read FWebClientActive;
|
property WebClientActive: Boolean read FWebClientActive;
|
||||||
|
|
||||||
@@ -256,8 +263,10 @@ type
|
|||||||
property OnCtun: TWebCmdCtun read FOnCtun write FOnCtun;
|
property OnCtun: TWebCmdCtun read FOnCtun write FOnCtun;
|
||||||
property OnNR: TWebCmdNRMode read FOnNR write FOnNR;
|
property OnNR: TWebCmdNRMode read FOnNR write FOnNR;
|
||||||
property OnNB: TWebCmdNBMode read FOnNB write FOnNB;
|
property OnNB: TWebCmdNBMode read FOnNB write FOnNB;
|
||||||
property OnSNB: TWebCmdSNB read FOnSNB write FOnSNB;
|
property OnSNB: TWebCmdSNB read FOnSNB write FOnSNB;
|
||||||
property OnANF: TWebCmdANF read FOnANF write FOnANF;
|
property OnANF: TWebCmdANF read FOnANF write FOnANF;
|
||||||
|
property OnFreqB: TWebCmdFreqB read FOnFreqB write FOnFreqB;
|
||||||
|
property OnActiveVfo: TWebCmdActiveVfo read FOnActiveVfo write FOnActiveVfo;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -949,6 +958,20 @@ begin
|
|||||||
On_ := JsonGetBool(Json, 'on', FANF);
|
On_ := JsonGetBool(Json, 'on', FANF);
|
||||||
FStateLock.Enter; FANF := On_; FStateLock.Leave;
|
FStateLock.Enter; FANF := On_; FStateLock.Leave;
|
||||||
if Assigned(FOnANF) then FOnANF(On_);
|
if Assigned(FOnANF) then FOnANF(On_);
|
||||||
|
end
|
||||||
|
else if Cmd = 'freq_b' then
|
||||||
|
begin
|
||||||
|
HzF := JsonGetFloat(Json, 'hz', FVfoB);
|
||||||
|
FStateLock.Enter; FVfoB := HzF; FStateLock.Leave;
|
||||||
|
if Assigned(FOnFreqB) then FOnFreqB(HzF);
|
||||||
|
end
|
||||||
|
else if Cmd = 'set_active_vfo' then
|
||||||
|
begin
|
||||||
|
ModeValue := JsonGetInt(Json, 'idx', FActiveVfo);
|
||||||
|
if ModeValue < 0 then ModeValue := 0;
|
||||||
|
if ModeValue > 1 then ModeValue := 1;
|
||||||
|
FStateLock.Enter; FActiveVfo := ModeValue; FStateLock.Leave;
|
||||||
|
if Assigned(FOnActiveVfo) then FOnActiveVfo(ModeValue);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -1038,14 +1061,16 @@ begin
|
|||||||
FStateLock.Enter;
|
FStateLock.Enter;
|
||||||
try
|
try
|
||||||
Result := Format(
|
Result := Format(
|
||||||
'{"vfo_a_hz":%.0f,"mode":%d,"mode_name":"%s",' +
|
'{"vfo_a_hz":%.0f,"vfo_b_hz":%.0f,"active_vfo":%d,' +
|
||||||
|
'"mode":%d,"mode_name":"%s",' +
|
||||||
'"filter":%d,"filter_bw":%d,' +
|
'"filter":%d,"filter_bw":%d,' +
|
||||||
'"agc_mode":%d,"agc_top":%d,' +
|
'"agc_mode":%d,"agc_top":%d,' +
|
||||||
'"span_hz":%.0f,"center_hz":%.0f,"volume":%d,' +
|
'"span_hz":%.0f,"center_hz":%.0f,"volume":%d,' +
|
||||||
'"wf_agc":%s,"wf_nf":%s,"band_idx":%d,"smeter_dbm":%.1f,' +
|
'"wf_agc":%s,"wf_nf":%s,"band_idx":%d,"smeter_dbm":%.1f,' +
|
||||||
'"running":%s,"mute":%s,"ctun":%s,' +
|
'"running":%s,"mute":%s,"ctun":%s,' +
|
||||||
'"nr_mode":%d,"nr":%s,"nb_mode":%d,"nb":%s,"snb":%s,"anf":%s,"connected":%s}',
|
'"nr_mode":%d,"nr":%s,"nb_mode":%d,"nb":%s,"snb":%s,"anf":%s,"connected":%s}',
|
||||||
[FFreq, FMode, MODE_N[FMode mod 8],
|
[FFreq, FVfoB, FActiveVfo,
|
||||||
|
FMode, MODE_N[FMode mod 8],
|
||||||
FFilterIdx, FFilterBW,
|
FFilterIdx, FFilterBW,
|
||||||
FAGCMode, FAGCTop,
|
FAGCMode, FAGCTop,
|
||||||
FSpanHz, FCenterHz, FVolume,
|
FSpanHz, FCenterHz, FVolume,
|
||||||
@@ -1125,7 +1150,8 @@ procedure TWebServer.PushSpectrum(
|
|||||||
TrxConnected: Boolean;
|
TrxConnected: Boolean;
|
||||||
TrxRunning, Muted, Ctun: Boolean;
|
TrxRunning, Muted, Ctun: Boolean;
|
||||||
NRMode, NBMode: Integer; SNB, ANF: Boolean;
|
NRMode, NBMode: Integer; SNB, ANF: Boolean;
|
||||||
CenterHz: Double; FilterIdx: Integer);
|
CenterHz: Double; FilterIdx: Integer;
|
||||||
|
VfoB: Double; ActiveVfo: Integer);
|
||||||
var N, i: Integer;
|
var N, i: Integer;
|
||||||
begin
|
begin
|
||||||
if not FRunning then Exit;
|
if not FRunning then Exit;
|
||||||
@@ -1155,11 +1181,12 @@ begin
|
|||||||
FANF := ANF;
|
FANF := ANF;
|
||||||
FCenterHz := CenterHz;
|
FCenterHz := CenterHz;
|
||||||
FFilterIdx := FilterIdx;
|
FFilterIdx := FilterIdx;
|
||||||
|
FVfoB := VfoB;
|
||||||
|
FActiveVfo := ActiveVfo;
|
||||||
finally
|
finally
|
||||||
FStateLock.Leave;
|
FStateLock.Leave;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ ═══════════════════════════════════════════════════════════════════════════
|
{ ═══════════════════════════════════════════════════════════════════════════
|
||||||
Stub-методы (реализация встроена в HandleClient)
|
Stub-методы (реализация встроена в HandleClient)
|
||||||
═══════════════════════════════════════════════════════════════════════════ }
|
═══════════════════════════════════════════════════════════════════════════ }
|
||||||
|
|||||||
Reference in New Issue
Block a user