mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-26 04:47:35 +00:00
Save/restore CTun, AGC, FM step per XVTR slot; add FM step to web
- Add LastCTun, LastAGCMode, LastAGCTop to TXvtrEntry; SaveCurrentBand now captures all three in the XVTR branch; ActivateXvtrBand restores them on slot switch/startup (previously CTun was always reset to False and AGC was never saved/restored for XVTR) - WebServer: expose fmstep_idx in state JSON so web clients sync the FM step from desktop state - WebPageHtml: swap stepSel options to FM steps (6.25k/12.5k/20k/25k) when mode=FM, restore HF steps when leaving FM; syncs fmstep_idx from server on connect and mode change Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+20
-8
@@ -1030,6 +1030,9 @@ begin
|
|||||||
FXvtrSettings.Entries[FCurrentXvtr].LastCTCSSToneIdx := FFMCTCSSToneIdx;
|
FXvtrSettings.Entries[FCurrentXvtr].LastCTCSSToneIdx := FFMCTCSSToneIdx;
|
||||||
FXvtrSettings.Entries[FCurrentXvtr].LastFMStepOn := FFMStepOn;
|
FXvtrSettings.Entries[FCurrentXvtr].LastFMStepOn := FFMStepOn;
|
||||||
FXvtrSettings.Entries[FCurrentXvtr].LastFMStepIdx := FFMStepIdx;
|
FXvtrSettings.Entries[FCurrentXvtr].LastFMStepIdx := FFMStepIdx;
|
||||||
|
FXvtrSettings.Entries[FCurrentXvtr].LastCTun := FCTun;
|
||||||
|
FXvtrSettings.Entries[FCurrentXvtr].LastAGCMode := FAGCMode;
|
||||||
|
FXvtrSettings.Entries[FCurrentXvtr].LastAGCTop := FAGCTop;
|
||||||
end;
|
end;
|
||||||
FSettings.SaveXvtr(FDevMAC, FXvtrSettings);
|
FSettings.SaveXvtr(FDevMAC, FXvtrSettings);
|
||||||
Exit;
|
Exit;
|
||||||
@@ -4801,6 +4804,7 @@ begin
|
|||||||
FBtnFMSteps[OldIdx].Active := False;
|
FBtnFMSteps[OldIdx].Active := False;
|
||||||
if FBtnFMSteps[Idx] <> nil then
|
if FBtnFMSteps[Idx] <> nil then
|
||||||
FBtnFMSteps[Idx].Active := True;
|
FBtnFMSteps[Idx].Active := True;
|
||||||
|
if FWebServer <> nil then FWebServer.FMStepIdx := Idx;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.BtnFMStepClick(Sender: TObject);
|
procedure TMainForm.BtnFMStepClick(Sender: TObject);
|
||||||
@@ -6243,17 +6247,25 @@ begin
|
|||||||
FFMStepOn := E.LastFMStepOn;
|
FFMStepOn := E.LastFMStepOn;
|
||||||
SetFMStep(E.LastFMStepIdx);
|
SetFMStep(E.LastFMStepIdx);
|
||||||
if BtnFMStep <> nil then StyleButton(BtnFMStep, FFMStepOn);
|
if BtnFMStep <> nil then StyleButton(BtnFMStep, FFMStepOn);
|
||||||
// VFO на видимую частоту; FCenterFreq тоже visible (как и для HF — DDC переход
|
// AGC
|
||||||
// в IF происходит только в момент UpdateState/SetRunAndFreq через XvtrTranslate).
|
FAGCMode := E.LastAGCMode;
|
||||||
|
FAGCTop := E.LastAGCTop;
|
||||||
|
for i := 0 to 4 do StyleButton(BtnAGCMode[i], i = FAGCMode);
|
||||||
|
TrkAGC.Position := FAGCTop;
|
||||||
|
LblAGCTop.Caption := Format('%ddB', [FAGCTop]);
|
||||||
|
if FWDSPReady then
|
||||||
|
begin
|
||||||
|
FDSPEngine.SetAGCTop(FAGCTop);
|
||||||
|
FDSPEngine.SetAGC(TWDSPAGCMode(FAGCMode), 50.0);
|
||||||
|
end;
|
||||||
|
FSpecView.AGCTop := FAGCTop;
|
||||||
|
// CTUN
|
||||||
|
FCTun := E.LastCTun;
|
||||||
|
StyleButton(BtnCTun, FCTun);
|
||||||
|
// VFO на видимую частоту; FCenterFreq тоже visible
|
||||||
FVfoA := Vis;
|
FVfoA := Vis;
|
||||||
FCenterFreq := Vis;
|
FCenterFreq := Vis;
|
||||||
FreqDispA.Frequency := Round(FVfoA);
|
FreqDispA.Frequency := Round(FVfoA);
|
||||||
// Сбросить CTUN — не имеет смысла в начальном состоянии XVTR-band
|
|
||||||
if FCTun then
|
|
||||||
begin
|
|
||||||
FCTun := False;
|
|
||||||
StyleButton(BtnCTun, False);
|
|
||||||
end;
|
|
||||||
if FWDSPReady then FDSPEngine.SetShift(0.0);
|
if FWDSPReady then FDSPEngine.SetShift(0.0);
|
||||||
// Применяем драйв (если есть кастомный TX power для этого XVTR)
|
// Применяем драйв (если есть кастомный TX power для этого XVTR)
|
||||||
// Сделано на стороне ActiveTXFreqHz / TX path — здесь только обновляем дисплей.
|
// Сделано на стороне ActiveTXFreqHz / TX path — здесь только обновляем дисплей.
|
||||||
|
|||||||
@@ -144,6 +144,9 @@ type
|
|||||||
LastCTCSSToneIdx: Integer;
|
LastCTCSSToneIdx: Integer;
|
||||||
LastFMStepOn: Boolean;
|
LastFMStepOn: Boolean;
|
||||||
LastFMStepIdx: Integer;
|
LastFMStepIdx: Integer;
|
||||||
|
LastCTun: Boolean;
|
||||||
|
LastAGCMode: Integer;
|
||||||
|
LastAGCTop: Integer;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TXvtrSettings = record
|
TXvtrSettings = record
|
||||||
@@ -1121,6 +1124,9 @@ begin
|
|||||||
X.Entries[i].LastCTCSSToneIdx := 0;
|
X.Entries[i].LastCTCSSToneIdx := 0;
|
||||||
X.Entries[i].LastFMStepOn := True;
|
X.Entries[i].LastFMStepOn := True;
|
||||||
X.Entries[i].LastFMStepIdx := 3; // 25 kHz
|
X.Entries[i].LastFMStepIdx := 3; // 25 kHz
|
||||||
|
X.Entries[i].LastCTun := False;
|
||||||
|
X.Entries[i].LastAGCMode := 1;
|
||||||
|
X.Entries[i].LastAGCTop := 90;
|
||||||
end;
|
end;
|
||||||
// Шаблоны (пользователь должен сам Enable, чтобы трансверторы появились)
|
// Шаблоны (пользователь должен сам Enable, чтобы трансверторы появились)
|
||||||
X.Entries[0].ButtonText := '2m';
|
X.Entries[0].ButtonText := '2m';
|
||||||
@@ -1174,6 +1180,9 @@ begin
|
|||||||
X.Entries[i].LastCTCSSToneIdx := EnsureRange(JI(EObj, 'last_ctcss_idx', X.Entries[i].LastCTCSSToneIdx), 0, 37);
|
X.Entries[i].LastCTCSSToneIdx := EnsureRange(JI(EObj, 'last_ctcss_idx', X.Entries[i].LastCTCSSToneIdx), 0, 37);
|
||||||
X.Entries[i].LastFMStepOn := JB(EObj, 'last_fmstep_on', X.Entries[i].LastFMStepOn);
|
X.Entries[i].LastFMStepOn := JB(EObj, 'last_fmstep_on', X.Entries[i].LastFMStepOn);
|
||||||
X.Entries[i].LastFMStepIdx := EnsureRange(JI(EObj, 'last_fmstep_idx', X.Entries[i].LastFMStepIdx), 0, 3);
|
X.Entries[i].LastFMStepIdx := EnsureRange(JI(EObj, 'last_fmstep_idx', X.Entries[i].LastFMStepIdx), 0, 3);
|
||||||
|
X.Entries[i].LastCTun := JB(EObj, 'last_ctun', X.Entries[i].LastCTun);
|
||||||
|
X.Entries[i].LastAGCMode := EnsureRange(JI(EObj, 'last_agc_mode', X.Entries[i].LastAGCMode), 0, 4);
|
||||||
|
X.Entries[i].LastAGCTop := EnsureRange(JI(EObj, 'last_agc_top', X.Entries[i].LastAGCTop), 0, 120);
|
||||||
// Валидация: LastFreq должен быть в [FreqBegin..FreqEnd]
|
// Валидация: LastFreq должен быть в [FreqBegin..FreqEnd]
|
||||||
if (X.Entries[i].LastFreq < X.Entries[i].FreqBegin) or
|
if (X.Entries[i].LastFreq < X.Entries[i].FreqBegin) or
|
||||||
(X.Entries[i].LastFreq > X.Entries[i].FreqEnd) then
|
(X.Entries[i].LastFreq > X.Entries[i].FreqEnd) then
|
||||||
@@ -1212,6 +1221,9 @@ begin
|
|||||||
JW(EObj, 'last_ctcss_idx',X.Entries[i].LastCTCSSToneIdx);
|
JW(EObj, 'last_ctcss_idx',X.Entries[i].LastCTCSSToneIdx);
|
||||||
JW(EObj, 'last_fmstep_on', X.Entries[i].LastFMStepOn);
|
JW(EObj, 'last_fmstep_on', X.Entries[i].LastFMStepOn);
|
||||||
JW(EObj, 'last_fmstep_idx',X.Entries[i].LastFMStepIdx);
|
JW(EObj, 'last_fmstep_idx',X.Entries[i].LastFMStepIdx);
|
||||||
|
JW(EObj, 'last_ctun', X.Entries[i].LastCTun);
|
||||||
|
JW(EObj, 'last_agc_mode', X.Entries[i].LastAGCMode);
|
||||||
|
JW(EObj, 'last_agc_top', X.Entries[i].LastAGCTop);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -216,6 +216,9 @@ begin
|
|||||||
'const stEl=document.getElementById("stEl"),latEl=document.getElementById("latEl");' +
|
'const stEl=document.getElementById("stEl"),latEl=document.getElementById("latEl");' +
|
||||||
|
|
||||||
'let cur={},vfoA=14200000,vfoB=14200000,activeVfo="A";' +
|
'let cur={},vfoA=14200000,vfoB=14200000,activeVfo="A";' +
|
||||||
|
'const FM_STEP_HZ=[6250,12500,20000,25000];' +
|
||||||
|
'const FM_STEP_LBL=["6.25k","12.5k","20k","25k"];' +
|
||||||
|
'let fmStepIdx=3,lastHFStep=100,inFMStep=false;' +
|
||||||
'let localCenter=14200000,localSpan=192000;' + // локальные копии — обновляются мгновенно при действии пользователя
|
'let localCenter=14200000,localSpan=192000;' + // локальные копии — обновляются мгновенно при действии пользователя
|
||||||
'let dragging=false,dragX0=0,dragCen0=0,dragVfo0=0,dragMs=0,wheelActiveTs=0;' +
|
'let dragging=false,dragX0=0,dragCen0=0,dragVfo0=0,dragMs=0,wheelActiveTs=0;' +
|
||||||
'let hoverX=-1,wTarget="",markerOn=false,markerX=0;' +
|
'let hoverX=-1,wTarget="",markerOn=false,markerX=0;' +
|
||||||
@@ -566,6 +569,7 @@ begin
|
|||||||
'if(!isPending("freq")){vfoA=cur.vfo_a_hz||vfoA;}' +
|
'if(!isPending("freq")){vfoA=cur.vfo_a_hz||vfoA;}' +
|
||||||
'if(msg.vfo_b_hz!=null)vfoB=msg.vfo_b_hz;' +
|
'if(msg.vfo_b_hz!=null)vfoB=msg.vfo_b_hz;' +
|
||||||
'if(msg.freq_mhz_digits!=null){freqMhzDigits=Math.max(3,Math.min(5,msg.freq_mhz_digits|0));freqMaxHz=Math.pow(10,freqMhzDigits+6)-1;}' +
|
'if(msg.freq_mhz_digits!=null){freqMhzDigits=Math.max(3,Math.min(5,msg.freq_mhz_digits|0));freqMaxHz=Math.pow(10,freqMhzDigits+6)-1;}' +
|
||||||
|
'if(msg.fmstep_idx!=null){fmStepIdx=Math.max(0,Math.min(3,msg.fmstep_idx|0));if(inFMStep)syncStepSel();}' +
|
||||||
'if(msg.active_vfo!=null&&!isPending("freq")){' +
|
'if(msg.active_vfo!=null&&!isPending("freq")){' +
|
||||||
'const sv=msg.active_vfo===0?"A":"B";' +
|
'const sv=msg.active_vfo===0?"A":"B";' +
|
||||||
'if(sv!==activeVfo){activeVfo=sv;' +
|
'if(sv!==activeVfo){activeVfo=sv;' +
|
||||||
@@ -583,7 +587,7 @@ begin
|
|||||||
'if(!isPending("mox"))updMoxBtn(!!cur.transmitting);' +
|
'if(!isPending("mox"))updMoxBtn(!!cur.transmitting);' +
|
||||||
'if(!isPending("tun"))updToneBtn(!!cur.tuning);' +
|
'if(!isPending("tun"))updToneBtn(!!cur.tuning);' +
|
||||||
'if(!isPending("drive")){const ts=q("txSl");if(ts&&!ts.matches(":active")){ts.value=cur.drive||0;var tv=q("txVal");if(tv)tv.textContent=cur.drive||0;}}' +
|
'if(!isPending("drive")){const ts=q("txSl");if(ts&&!ts.matches(":active")){ts.value=cur.drive||0;var tv=q("txVal");if(tv)tv.textContent=cur.drive||0;}}' +
|
||||||
'if(!isPending("mode")){syncModGrp(cur.mode||0);}' +
|
'if(!isPending("mode")){syncModGrp(cur.mode||0);syncStepSel();}' +
|
||||||
'if(!isPending("attn")){const at=q("attSel");if(at)at.value=cur.attn_idx||0;}' +
|
'if(!isPending("attn")){const at=q("attSel");if(at)at.value=cur.attn_idx||0;}' +
|
||||||
'updBand(activeVfo==="A"?(cur.vfo_a_hz||0):(vfoB||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));' +
|
||||||
@@ -642,6 +646,13 @@ begin
|
|||||||
'if(smCv.width!==(sw*dpr|0)||smCv.height!==(sh*dpr|0)){smCv.width=sw*dpr|0;smCv.height=sh*dpr|0;mctx.setTransform(dpr,0,0,dpr,0,0);}' +
|
'if(smCv.width!==(sw*dpr|0)||smCv.height!==(sh*dpr|0)){smCv.width=sw*dpr|0;smCv.height=sh*dpr|0;mctx.setTransform(dpr,0,0,dpr,0,0);}' +
|
||||||
'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);}}' +
|
'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 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=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;}' +
|
'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 activeVfoHz(){return activeVfo==="A"?vfoA:vfoB;}' +
|
'function activeVfoHz(){return activeVfo==="A"?vfoA:vfoB;}' +
|
||||||
'function setActiveVfoHz(f){if(activeVfo==="A")vfoA=f;else vfoB=f;}' +
|
'function setActiveVfoHz(f){if(activeVfo==="A")vfoA=f;else vfoB=f;}' +
|
||||||
|
|||||||
+7
-2
@@ -212,6 +212,7 @@ type
|
|||||||
FFilterIdx: Integer;
|
FFilterIdx: Integer;
|
||||||
FVfoB: Double;
|
FVfoB: Double;
|
||||||
FActiveVfo: Integer; // 0=A, 1=B
|
FActiveVfo: Integer; // 0=A, 1=B
|
||||||
|
FFMStepIdx: Integer;
|
||||||
FStateLock: TCriticalSection;
|
FStateLock: TCriticalSection;
|
||||||
|
|
||||||
// ── Callbacks ──
|
// ── Callbacks ──
|
||||||
@@ -304,6 +305,7 @@ type
|
|||||||
|
|
||||||
property WebClientActive: Boolean read FWebClientActive;
|
property WebClientActive: Boolean read FWebClientActive;
|
||||||
property FreqMhzDigits: Integer read FFreqMhzDigits write FFreqMhzDigits;
|
property FreqMhzDigits: Integer read FFreqMhzDigits write FFreqMhzDigits;
|
||||||
|
property FMStepIdx: Integer read FFMStepIdx write FFMStepIdx;
|
||||||
|
|
||||||
property OnFreq: TWebCmdFreq read FOnFreq write FOnFreq;
|
property OnFreq: TWebCmdFreq read FOnFreq write FOnFreq;
|
||||||
property OnMode: TWebCmdMode read FOnMode write FOnMode;
|
property OnMode: TWebCmdMode read FOnMode write FOnMode;
|
||||||
@@ -427,6 +429,7 @@ begin
|
|||||||
// Начальные значения состояния
|
// Начальные значения состояния
|
||||||
FFreq := 14200000;
|
FFreq := 14200000;
|
||||||
FMode := 1;
|
FMode := 1;
|
||||||
|
FFMStepIdx := 3; // 25 kHz default
|
||||||
FFilterBW:= 2700;
|
FFilterBW:= 2700;
|
||||||
FAGCMode := 1;
|
FAGCMode := 1;
|
||||||
FAGCTop := 90;
|
FAGCTop := 90;
|
||||||
@@ -1296,7 +1299,8 @@ begin
|
|||||||
'"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,' +
|
||||||
'"transmitting":%s,"drive":%d,"attn_idx":%d,"tuning":%s,"dup":%s,' +
|
'"transmitting":%s,"drive":%d,"attn_idx":%d,"tuning":%s,"dup":%s,' +
|
||||||
'"fwd_w":%.1f,"swr":%.2f,"pa_max_power":%.0f,' +
|
'"fwd_w":%.1f,"swr":%.2f,"pa_max_power":%.0f,' +
|
||||||
'"xvtr_current":%d,"xvtr_bands":%s,"freq_mhz_digits":%d}',
|
'"xvtr_current":%d,"xvtr_bands":%s,"freq_mhz_digits":%d,' +
|
||||||
|
'"fmstep_idx":%d}',
|
||||||
[FFreq, FVfoB, FActiveVfo,
|
[FFreq, FVfoB, FActiveVfo,
|
||||||
FMode, MODE_N[FMode mod 8],
|
FMode, MODE_N[FMode mod 8],
|
||||||
FFilterIdx, FFilterBW,
|
FFilterIdx, FFilterBW,
|
||||||
@@ -1321,7 +1325,8 @@ begin
|
|||||||
BoolToStr(FTuning, 'true', 'false'),
|
BoolToStr(FTuning, 'true', 'false'),
|
||||||
BoolToStr(FDuplex, 'true', 'false'),
|
BoolToStr(FDuplex, 'true', 'false'),
|
||||||
FFwdW, FSWR, FPAMaxPower,
|
FFwdW, FSWR, FPAMaxPower,
|
||||||
FCurrentXvtr, XvtrJson, FFreqMhzDigits
|
FCurrentXvtr, XvtrJson, FFreqMhzDigits,
|
||||||
|
FFMStepIdx
|
||||||
], FS);
|
], FS);
|
||||||
finally
|
finally
|
||||||
FStateLock.Leave;
|
FStateLock.Leave;
|
||||||
|
|||||||
Reference in New Issue
Block a user