mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-26 19:57:33 +00:00
fix(web): детализация спектра/водопада — честный размер кадра, поток 'W', настройка разрешения
Веб-пульт рисовал заметно грубее десктопа, и часть причин копилась давно. Клиент вообще не разбирал бинарный фрейм 0x57: водопад рисовался из кадра СПЕКТРА, а собственный поток водопада (свой детектор и усреднение wf_avg_time_ms) уходил в никуда, занимая половину трафика. Теперь водопад идёт из своего потока. Адаптер слал константу 1024 вместо реального числа точек. Разрешение анализатора = ширина панадаптера, и при узкой панели (грид из двух колонок, маленькое окно) кадр короче — хвост буфера, нули = 0 dBm, уезжал клиенту белой полосой, а частотная шкала сжималась. В демоне это обходили DAEMON_SPEC_WIDTH. Длина фрейма теперь равна числу заполненных точек; клиент и так считал N из byteLength. Децимация под потолок протокола делила «i*Count div N» — на нецелом отношении соседние группы получались по 1 и 2 точки, а смещение max-детектора зависит от размера группы: по спектру шла гребёнка ±3 дБ, в водопаде — вертикальная рябь. Заменено на целочисленный фактор (группы одинаковой ширины). Водопад терял 2 кадра из 3: анализатор даёт до 60 кадров/с, web шлёт 20 строк/с, а бралась просто «последняя». Введено двухступенчатое накопление max-hold (DSP-поток → PushState → PushLoop) по идиоме TWaterfallView.SetWaterfallData — короткие посылки CW/FT8 больше не проваливаются между строками. Отдельно исправлено в headless: PushState демона (20 Гц) и PushLoop (20 Гц) идут вразнобой, и на дрейфе фаз одна и та же строка уезжала клиенту дважды (водопад двоил и полз рывками), а на остановленном RX прокручивалась застывшая строка. Фрейм 'W' теперь отправляется только при реально пришедшем от DSP кадре (FWfAccumFresh → WfNew → FWfFresh). Спектр так не гейтится — это живая кривая. Рендер в браузере: значение бина для пикселя берётся максимумом по диапазону вместо «ближайшего» (узкие сигналы мерцали и пропадали на узком экране), при бинах реже пикселей — интерполяция; сглаживание спектра вынесено отдельным проходом по всем бинам (в цикле по пикселям часть бинов не обновлялась вовсе); строка водопада рисуется из сырых бинов, IIR ~200 мс остался только для слежения за уровнями AGC — он размазывал водопад по вертикали. Потолок точек кадра вынесен в настройку web.spec_pixels (Settings → Advanced → Web Server → Spectrum: 1024/2048/4096, дефолт 1024). Буферы кадра и WS-фреймы выросли под 4096 (WEB_SPEC_MAX, синхронно с WDSPEngine.SPECTRUM_PIXELS). В демоне эта же настройка задаёт ширину анализатора — рендера там нет, поэтому DAEMON_SPEC_WIDTH убран. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+37
-12
@@ -66,7 +66,7 @@ type
|
||||
TOnWfAGCNFChange = procedure(WfAGC, WfNF: Boolean) of object;
|
||||
TOnADCChange = procedure(Dither, Random: Boolean) of object;
|
||||
TOnWebSettingsChange = procedure(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr, User, Pass: string) of object;
|
||||
const BindAddr, User, Pass: string; SpecPixels: Integer) of object;
|
||||
TOnCATChange = procedure(
|
||||
const SerEnabled: array of Boolean;
|
||||
const SerPort: array of string;
|
||||
@@ -382,6 +382,7 @@ type
|
||||
FEdWebBind: TFlatEdit;
|
||||
FEdWebUser: TFlatEdit;
|
||||
FEdWebPass: TFlatEdit;
|
||||
FCmbWebRes: TFlatComboBox;
|
||||
|
||||
// ---- Close button ----
|
||||
FBtnClose: TFlatButton;
|
||||
@@ -581,7 +582,7 @@ type
|
||||
procedure LoadSpecMSAA(Samples: Integer);
|
||||
procedure LoadADCSettings(Dither, Random: Boolean);
|
||||
procedure LoadWebSettings(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr, User, Pass: string);
|
||||
const BindAddr, User, Pass: string; SpecPixels: Integer);
|
||||
|
||||
property OnCATChange: TOnCATChange read FOnCATChange write FOnCATChange;
|
||||
property OnSliceCATChange: TOnSliceCATChange read FOnSliceCATChange write FOnSliceCATChange;
|
||||
@@ -2497,7 +2498,8 @@ var
|
||||
Chk: TFlatCheckBox;
|
||||
Ed: TFlatEdit;
|
||||
Spin: TFlatSpinEdit;
|
||||
Y: Integer;
|
||||
Lbl: TLabel;
|
||||
Y, i: Integer;
|
||||
begin
|
||||
with TLabel.Create(Self) do
|
||||
begin
|
||||
@@ -2536,7 +2538,7 @@ begin
|
||||
FChkRandom := Chk;
|
||||
|
||||
// ── Web Server ────────────────────────────────────────────────────────────
|
||||
Grp := MakeGroupPanel(FPageAdvanced, 'Web Server', MARGIN, 230, 500, 252);
|
||||
Grp := MakeGroupPanel(FPageAdvanced, 'Web Server', MARGIN, 230, 500, 318);
|
||||
|
||||
Y := R1;
|
||||
Chk := TFlatCheckBox.Create(Self);
|
||||
@@ -2604,6 +2606,18 @@ begin
|
||||
Ed.Text := 'hpsdr';
|
||||
Ed.OnChange := OnWebAnyChange;
|
||||
FEdWebPass := Ed;
|
||||
|
||||
// Потолок точек в кадре спектра/водопада, уходящем веб-клиенту. Реально
|
||||
// отправляется min(этот потолок, разрешение анализатора = ширина
|
||||
// панадаптера); выше — децимация max-детектором.
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Spectrum:', GRP_PAD, Y + 4, LBL_W);
|
||||
FCmbWebRes := MakeCombo(Grp, GRP_PAD + LBL_W + 12, Y, 130, OnWebAnyChange);
|
||||
for i := 0 to High(DISPLAY_RES_NAMES) do FCmbWebRes.Items.Add(DISPLAY_RES_NAMES[i]);
|
||||
FCmbWebRes.ItemIndex := 0;
|
||||
Lbl := MakeLbl(Grp, '1024 px ~160 KB/s, 4096 px ~650 KB/s per client',
|
||||
GRP_PAD + LBL_W + 12, Y + ROW_H - 8, 320);
|
||||
Lbl.Font.Size := 8;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnADCChkChange(Sender: TObject);
|
||||
@@ -2614,15 +2628,21 @@ begin
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnWebAnyChange(Sender: TObject);
|
||||
var Res: Integer;
|
||||
begin
|
||||
if FLoading then Exit;
|
||||
if Assigned(FOnWebSettingsChange) then
|
||||
FOnWebSettingsChange(
|
||||
FChkWebEnabled.Checked,
|
||||
FEdWebPort.Value,
|
||||
FEdWebBind.Text,
|
||||
FEdWebUser.Text,
|
||||
FEdWebPass.Text);
|
||||
if not Assigned(FOnWebSettingsChange) then Exit;
|
||||
if (FCmbWebRes.ItemIndex >= 0) and (FCmbWebRes.ItemIndex <= High(DISPLAY_RES_VALUES)) then
|
||||
Res := DISPLAY_RES_VALUES[FCmbWebRes.ItemIndex]
|
||||
else
|
||||
Res := DISPLAY_RES_VALUES[0];
|
||||
FOnWebSettingsChange(
|
||||
FChkWebEnabled.Checked,
|
||||
FEdWebPort.Value,
|
||||
FEdWebBind.Text,
|
||||
FEdWebUser.Text,
|
||||
FEdWebPass.Text,
|
||||
Res);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadADCSettings(Dither, Random: Boolean);
|
||||
@@ -2637,7 +2657,8 @@ begin
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadWebSettings(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr, User, Pass: string);
|
||||
const BindAddr, User, Pass: string; SpecPixels: Integer);
|
||||
var i: Integer;
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
@@ -2646,6 +2667,10 @@ begin
|
||||
FEdWebBind.Text := BindAddr;
|
||||
FEdWebUser.Text := User;
|
||||
FEdWebPass.Text := Pass;
|
||||
FCmbWebRes.ItemIndex := 0;
|
||||
for i := 0 to High(DISPLAY_RES_VALUES) do
|
||||
if DISPLAY_RES_VALUES[i] = SpecPixels then
|
||||
begin FCmbWebRes.ItemIndex := i; Break; end;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user