mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(dsp): режимы WFM, DIGI-U и DIGI-L
WFM использует новые wfmd/wfmmod из нашего форка WDSP. Демодулятор —
квадратурный дискриминатор на dsp_rate, поэтому при штатных 48 кГц
девиация 75 кГц не помещается в полосу. В WFM цепь RXA поднимается до
WFMDSPRate (наибольший FAudioRate*2^k <= 192 кГц, делящий SampleRate
нацело), rsmpin децимирует вход сам. Выше 192 кГц смысла нет: энергия
демодулированного тона выходит на полку (33.5k при 192 кГц против 31.8k
при 576 кГц), а FFT дорожает.
Две ловушки WDSP, найденные по дороге:
* firmin.c: nfor = nc / size, idxmask = nfor - 1. Все fircore создаются
с nc = max(2048, dsp_size), поэтому смена dsp_size без RXASetNC даёт
nfor = 0 — пустой массив планов и падение в fftw_execute.
* fir.c/bandpass.c планируют с FFTW_PATIENT, а wisdom.c пишет в
wdspWisdom00 только степени двойки. При SampleRate 576 кГц наивный
dsp_size = in_size = 12288 требовал FFT на 24576 точек — FFTW мерил
план 25 секунд. Отсюда потолок 192 кГц и dsp_size = степень двойки.
RXA и TXA гейтятся раздельно: рейты TXA (48 кГц вход, 192 кГц выход) от
SampleRate не зависят, поэтому wfmmod уходит на широкий dsp_rate даже
когда приёмник остался на 48 кГц.
Заодно исправлен WDSP_SAM: было 12, а RXA_SAM = 10. Раньше 12 было вне
диапазона и WDSP молча игнорировал (SAM работал как DSB); после появления
RXA_WFM = 12 это включало бы WFM-демодулятор вместо SAM. txaMode
расходится с rxaMode после DRM (WFM: RXA 12, TXA 14) — отсюда ModeToWDSPTX.
DIGI-U/DIGI-L получили свою таблицу фильтров (3.0k…300) и полосу от нуля,
а не от среза на 100 Гц, как у SSB. В CAT они замаплены на коды Kenwood 6
(FSK) и 9 (FSK-R) — раньше эти коды сваливались в USB/LSB.
Новые индексы дописаны в конец (DIGU=8, DIGL=9, WFM=10): индекс режима
уходит в settings.json, band-cache, CAT и web-API. В VfoOverlay пришлось
развести хит-коды — режимы кодировались как -(I+1) и налезли на HIT_NR/
HIT_NB/HIT_SNB, а полосы кодировались значением BW, и WFM-овские
120000/160000/200000 налезли на HIT_AGC_PICK_BASE/HIT_DEV_BASE.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+79
-24
@@ -64,7 +64,7 @@ const
|
||||
WDSP_WISDOM_FILE = 'wdspWisdom00';
|
||||
|
||||
BAND_COUNT = 11;
|
||||
MODE_COUNT = 8;
|
||||
MODE_COUNT = 11;
|
||||
// Полосы/дефолты фильтров (FILT_COUNT, FILT_*_BW, FILT_*_DEF, FILT_FM_*)
|
||||
// перенесены в Settings.pas — нужны TRadioController. Здесь остаются только
|
||||
// UI-подписи кнопок (NAMES).
|
||||
@@ -76,7 +76,10 @@ const
|
||||
('16k','12k','10k','8.0k','6.6k','5.2k','4.0k','3.1k','2.9k','2.4k');
|
||||
FILT_AM_NAMES: array[0..FILT_COUNT-1] of string =
|
||||
('20k','18k','16k','12k','10k','9.0k','8.0k','7.0k','6.0k','5.0k');
|
||||
FILT_DIGI_NAMES: array[0..FILT_COUNT-1] of string =
|
||||
('3.0k','2.7k','2.4k','2.1k','1.8k','1.5k','1.2k','900','600','300');
|
||||
FILT_FM_NAMES: array[0..1] of string = ('NFM', 'FM');
|
||||
FILT_WFM_NAMES: array[0..FILT_WFM_COUNT-1] of string = ('200k', '160k', '120k');
|
||||
|
||||
// CTCSS tones (38 standard tones, no None — toggle via CTCSS button)
|
||||
// CTCSS_COUNT и CTCSS_TONES перенесены в FMRepeater (нужны TRadioController).
|
||||
@@ -97,7 +100,8 @@ const
|
||||
'160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m');
|
||||
|
||||
MODE_NAMES: array[0..MODE_COUNT-1] of string = (
|
||||
'LSB','USB','DSB','CWL','CWU','FM','AM','SAM');
|
||||
'LSB','USB','DSB','CWL','CWU','FM','AM','SAM','DIGU','DIGL','WFM');
|
||||
MODE_ROWS = 3; // 4 колонки; последний ряд неполный
|
||||
|
||||
SMETER_WIDTH_RATIO = 0.32;
|
||||
SMETER_MARGIN = 3;
|
||||
@@ -1551,6 +1555,8 @@ const
|
||||
BTN_H = 26;
|
||||
BTN_SM = 24;
|
||||
LEFT_W = 232;
|
||||
// Заголовок (16) + MODE_ROWS рядов с шагом 27 + нижний отступ.
|
||||
MODE_PANEL_H = 16 + (MODE_ROWS - 1) * 27 + BTN_SM + 7;
|
||||
PLUTO_AGC_H = 64; // высота отдельного Pluto RF-AGC блока (под AGC)
|
||||
TOP_VFO_W = 244;
|
||||
TOP_VFO_FREQ_H = 47;
|
||||
@@ -2005,10 +2011,10 @@ begin
|
||||
|
||||
Inc(Y, 76);
|
||||
|
||||
// Mode selector
|
||||
// Mode selector — MODE_ROWS рядов по 4 кнопки (последний неполный).
|
||||
PanelMode := TPanel.Create(Self);
|
||||
PanelMode.Parent := PanelLeft;
|
||||
PanelMode.SetBounds(0, Y, LEFT_W, 74);
|
||||
PanelMode.SetBounds(0, Y, LEFT_W, MODE_PANEL_H);
|
||||
PanelMode.BevelOuter := bvNone;
|
||||
MakeLbl(PanelMode, 'MODE', 4, 2);
|
||||
for i := 0 to MODE_COUNT - 1 do
|
||||
@@ -2023,7 +2029,7 @@ begin
|
||||
StyleButton(B, i = FController.FMode);
|
||||
end;
|
||||
|
||||
Inc(Y, 76);
|
||||
Inc(Y, MODE_PANEL_H + 2);
|
||||
|
||||
// Filter selector (10 кнопок в 2 ряда по 5)
|
||||
PanelFilter := TPanel.Create(Self);
|
||||
@@ -4057,6 +4063,28 @@ begin
|
||||
if PanelFMCTCSS <> nil then PanelFMCTCSS.Visible := False;
|
||||
if PanelFMStep <> nil then begin CloseFMStepPopup; PanelFMStep.Visible := False; end;
|
||||
if PanelFMRpt <> nil then PanelFMRpt.Visible := False;
|
||||
|
||||
if FController.FMode = MODE_WFM then
|
||||
begin
|
||||
// WFM: полосы вещательного канала — три кнопки в один ряд.
|
||||
CloseCTCSSPopup;
|
||||
if FController.FFilter > FILT_WFM_COUNT - 1 then FController.FFilter := FILT_WFM_DEF;
|
||||
FController.FFilterBW := FILT_WFM_BW[FController.FFilter];
|
||||
for i := 0 to FILT_WFM_COUNT - 1 do
|
||||
begin
|
||||
BtnFilter[i].Caption := FILT_WFM_NAMES[i];
|
||||
BtnFilter[i].Left := LeftPanelButtonLeft(PanelFilter.Width, FILT_WFM_COUNT, i);
|
||||
BtnFilter[i].Width := LeftPanelButtonWidth(PanelFilter.Width, FILT_WFM_COUNT, i);
|
||||
BtnFilter[i].Top := 16;
|
||||
BtnFilter[i].Visible := True;
|
||||
StyleButton(BtnFilter[i], i = FController.FFilter);
|
||||
end;
|
||||
for i := FILT_WFM_COUNT to FILT_COUNT - 1 do BtnFilter[i].Visible := False;
|
||||
PanelFilter.Height := BtnFilter[0].Top + BtnFilter[0].Height + 4;
|
||||
RelayoutBelowBands;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
for i := 0 to FILT_COUNT - 1 do
|
||||
begin
|
||||
BtnFilter[i].Left := LeftPanelButtonLeft(PanelFilter.Width, 5, i mod 5);
|
||||
@@ -4066,15 +4094,23 @@ begin
|
||||
end;
|
||||
|
||||
case FController.FMode of
|
||||
0, 1: begin
|
||||
MODE_LSB, MODE_USB:
|
||||
begin
|
||||
for i := 0 to FILT_COUNT-1 do Names[i] := FILT_SSB_NAMES[i];
|
||||
DefIdx := FILT_SSB_DEF;
|
||||
end;
|
||||
2: begin
|
||||
MODE_DIGU, MODE_DIGL:
|
||||
begin
|
||||
for i := 0 to FILT_COUNT-1 do Names[i] := FILT_DIGI_NAMES[i];
|
||||
DefIdx := FILT_DIGI_DEF;
|
||||
end;
|
||||
MODE_DSB:
|
||||
begin
|
||||
for i := 0 to FILT_COUNT-1 do Names[i] := FILT_DSB_NAMES[i];
|
||||
DefIdx := FILT_DSB_DEF;
|
||||
end;
|
||||
3, 4: begin
|
||||
MODE_CWL, MODE_CWU:
|
||||
begin
|
||||
for i := 0 to FILT_COUNT-1 do Names[i] := FILT_CW_NAMES[i];
|
||||
DefIdx := FILT_CW_DEF;
|
||||
end;
|
||||
@@ -4086,9 +4122,14 @@ begin
|
||||
|
||||
FController.FFilter := DefIdx;
|
||||
case FController.FMode of
|
||||
0, 1: FController.FFilterBW := FILT_SSB_BW[DefIdx];
|
||||
2: FController.FFilterBW := FILT_DSB_BW[DefIdx];
|
||||
3, 4: FController.FFilterBW := FILT_CW_BW[DefIdx];
|
||||
MODE_LSB, MODE_USB:
|
||||
FController.FFilterBW := FILT_SSB_BW[DefIdx];
|
||||
MODE_DIGU, MODE_DIGL:
|
||||
FController.FFilterBW := FILT_DIGI_BW[DefIdx];
|
||||
MODE_DSB:
|
||||
FController.FFilterBW := FILT_DSB_BW[DefIdx];
|
||||
MODE_CWL, MODE_CWU:
|
||||
FController.FFilterBW := FILT_CW_BW[DefIdx];
|
||||
else FController.FFilterBW := FILT_AM_BW[DefIdx];
|
||||
end;
|
||||
|
||||
@@ -5190,8 +5231,10 @@ begin
|
||||
if Assigned(FVfoOverlay) and FVfoOverlay.Visible then
|
||||
begin
|
||||
case FController.FMode of
|
||||
MODE_LSB: begin LoHz := -FController.FFilterBW; HiHz := -100; end;
|
||||
MODE_USB: begin LoHz := 100; HiHz := FController.FFilterBW; end;
|
||||
MODE_LSB: begin LoHz := -FController.FFilterBW; HiHz := -100; end;
|
||||
MODE_USB: begin LoHz := 100; HiHz := FController.FFilterBW; end;
|
||||
MODE_DIGL: begin LoHz := -FController.FFilterBW; HiHz := 0; end;
|
||||
MODE_DIGU: begin LoHz := 0; HiHz := FController.FFilterBW; end;
|
||||
else begin LoHz := -FController.FFilterBW / 2; HiHz := FController.FFilterBW / 2; end;
|
||||
end;
|
||||
Ovls[n] := FVfoOverlay;
|
||||
@@ -5275,15 +5318,27 @@ begin
|
||||
end;
|
||||
FController.FMode := Mode;
|
||||
FController.FFilterBW := FilterBW;
|
||||
if Mode = MODE_FM then
|
||||
begin
|
||||
FController.FFilter := FILT_FM_DEF;
|
||||
for Idx := 0 to FILT_FM_COUNT - 1 do
|
||||
if FILT_FM_BW[Idx] = FilterBW then
|
||||
case Mode of
|
||||
MODE_FM:
|
||||
begin
|
||||
FController.FFilter := Idx;
|
||||
FController.FFMDeviation := FILT_FM_DEV[Idx];
|
||||
Break;
|
||||
FController.FFilter := FILT_FM_DEF;
|
||||
for Idx := 0 to FILT_FM_COUNT - 1 do
|
||||
if FILT_FM_BW[Idx] = FilterBW then
|
||||
begin
|
||||
FController.FFilter := Idx;
|
||||
FController.FFMDeviation := FILT_FM_DEV[Idx];
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
MODE_WFM:
|
||||
begin
|
||||
FController.FFilter := FILT_WFM_DEF;
|
||||
for Idx := 0 to FILT_WFM_COUNT - 1 do
|
||||
if FILT_WFM_BW[Idx] = FilterBW then
|
||||
begin
|
||||
FController.FFilter := Idx;
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
if FController.FWDSPReady then
|
||||
@@ -5576,9 +5631,9 @@ begin
|
||||
O := FindSliceFlag(SliceId);
|
||||
if (O = nil) or not FController.GetSlice(SliceId, S) then Exit;
|
||||
case S.Mode of
|
||||
MODE_LSB: BW := -S.FilterLo;
|
||||
MODE_USB: BW := S.FilterHi;
|
||||
else BW := S.FilterHi - S.FilterLo;
|
||||
MODE_LSB, MODE_DIGL: BW := -S.FilterLo;
|
||||
MODE_USB, MODE_DIGU: BW := S.FilterHi;
|
||||
else BW := S.FilterHi - S.FilterLo;
|
||||
end;
|
||||
O.SetState(S.Mode, BW, S.TargetHz, FController.SliceSMeter(SliceId));
|
||||
O.SetDSPState(0, 0, False, False, FController.FAGCMode);
|
||||
|
||||
Reference in New Issue
Block a user