mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(fm): add universal FM RAW decoder output
This commit is contained in:
+63
-22
@@ -25,7 +25,7 @@ interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Math, SyncObjs,
|
||||
BeaconDecoder,
|
||||
BeaconDecoder, RadioModes,
|
||||
WDSP;
|
||||
|
||||
const
|
||||
@@ -49,18 +49,22 @@ const
|
||||
// Наши индексы режимов (совпадают с кнопками MainForm).
|
||||
// Новые режимы дописываются В КОНЕЦ: индекс уходит в settings.json,
|
||||
// band-cache, CAT (ZZMD/ZZMV) и web-API.
|
||||
MODE_LSB = 0;
|
||||
MODE_USB = 1;
|
||||
MODE_DSB = 2;
|
||||
MODE_CWL = 3;
|
||||
MODE_CWU = 4;
|
||||
MODE_FM = 5;
|
||||
MODE_AM = 6;
|
||||
MODE_SAM = 7;
|
||||
MODE_DIGU = 8;
|
||||
MODE_DIGL = 9;
|
||||
MODE_WFM = 10;
|
||||
MODE_DMR = 11; // DMR receive: flat WDSP discriminator -> DMRDecoder
|
||||
MODE_LSB = RadioModes.MODE_LSB;
|
||||
MODE_USB = RadioModes.MODE_USB;
|
||||
MODE_DSB = RadioModes.MODE_DSB;
|
||||
MODE_CWL = RadioModes.MODE_CWL;
|
||||
MODE_CWU = RadioModes.MODE_CWU;
|
||||
MODE_FM = RadioModes.MODE_FM;
|
||||
MODE_AM = RadioModes.MODE_AM;
|
||||
MODE_SAM = RadioModes.MODE_SAM;
|
||||
MODE_DIGU = RadioModes.MODE_DIGU;
|
||||
MODE_DIGL = RadioModes.MODE_DIGL;
|
||||
MODE_WFM = RadioModes.MODE_WFM;
|
||||
MODE_DMR = RadioModes.MODE_DMR;
|
||||
MODE_FMRAW = RadioModes.MODE_FMRAW;
|
||||
MODE_MIN = RadioModes.MODE_MIN;
|
||||
MODE_MAX = RadioModes.MODE_MAX;
|
||||
MODE_COUNT = RadioModes.MODE_COUNT;
|
||||
|
||||
// Минимальная длина FIR у WDSP: create_* поднимают fircore с
|
||||
// nc = max(2048, dsp_size). В firmin.c nfor = nc / size, idxmask = nfor - 1,
|
||||
@@ -443,6 +447,7 @@ type
|
||||
FTXWfAvgTimeMS: Double;
|
||||
|
||||
FMode: Integer;
|
||||
FRawFMDeviation: Double;
|
||||
FSMeter: Double;
|
||||
FFilterLow: Integer;
|
||||
FFilterHigh: Integer;
|
||||
@@ -479,6 +484,7 @@ type
|
||||
procedure ApplyDSPRate(Mode: Integer);
|
||||
procedure ApplyRXWFMToChan(Chan: Integer);
|
||||
procedure ApplyRXDMRToChan(Chan: Integer);
|
||||
procedure ApplyRXRawToChan(Chan: Integer);
|
||||
procedure ApplyWFMSettings;
|
||||
// --- Мультислайсы ---
|
||||
function FindSliceIdx(Id: Integer): Integer; // -1 если нет
|
||||
@@ -615,6 +621,7 @@ type
|
||||
procedure SetSNB(Enable: Boolean);
|
||||
procedure SetANF(Enable: Boolean);
|
||||
procedure SetRXFMDeviation(Deviation: Double);
|
||||
procedure SetRawFMDeviation(Deviation: Double);
|
||||
procedure SetFMSquelch(Enable: Boolean; Level: Integer);
|
||||
procedure SetVolume(Vol: Double);
|
||||
procedure SetMute(Mute: Boolean);
|
||||
@@ -991,6 +998,7 @@ begin
|
||||
// de-emphasis. Those filters collapse the four DMR discriminator levels.
|
||||
// WFMD is a plain quadrature discriminator and permits flat data audio.
|
||||
MODE_DMR: Result := WDSP_RX_WFM;
|
||||
MODE_FMRAW: Result := WDSP_RX_WFM;
|
||||
else Result := WDSP_USB;
|
||||
end;
|
||||
end;
|
||||
@@ -999,7 +1007,7 @@ function TWDSPEngine.ModeToWDSPTX(Mode: Integer): Integer;
|
||||
// txaMode расходится с rxaMode после DRM — отдельный маппинг только для WFM.
|
||||
begin
|
||||
if Mode = MODE_WFM then Result := WDSP_TX_WFM
|
||||
else if Mode = MODE_DMR then Result := WDSP_FM
|
||||
else if Mode in [MODE_DMR, MODE_FMRAW] then Result := WDSP_FM
|
||||
else Result := ModeToWDSP(Mode);
|
||||
end;
|
||||
|
||||
@@ -1122,6 +1130,15 @@ begin
|
||||
SetRXAWFMDeemphRun (Chan, 0);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.ApplyRXRawToChan(Chan: Integer);
|
||||
begin
|
||||
// Decoder-grade discriminator: no broadcast de-emphasis or limiter.
|
||||
SetRXAWFMDeviation (Chan, FRawFMDeviation);
|
||||
SetRXAWFMAFFilter (Chan, 20.0, 7000.0);
|
||||
SetRXAWFMDeemphRun (Chan, 0);
|
||||
SetRXAWFMLimRun (Chan, 0);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.ApplyWFMSettings;
|
||||
begin
|
||||
ApplyRXWFMToChan(RXA_CHAN);
|
||||
@@ -1153,6 +1170,7 @@ begin
|
||||
MODE_DIGL: SetFilter(-3000, 0);
|
||||
MODE_WFM: SetFilter(-80000, 80000);
|
||||
MODE_DMR: SetFilter(-6250, 6250);
|
||||
MODE_FMRAW: SetFilter(-6250, 6250);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -1306,6 +1324,7 @@ begin
|
||||
FTXEQFreqs[10] := 3500;
|
||||
FTXAMCarrier := 0.5;
|
||||
FTXFMDeviation := 5000.0;
|
||||
FRawFMDeviation := 2500.0;
|
||||
FTXFMLowCut := 300;
|
||||
FTXFMHighCut := 3000;
|
||||
FTXFMEmphPos := 1;
|
||||
@@ -2312,6 +2331,7 @@ begin
|
||||
SetRXAMode(Chan, ModeToWDSP(FSlices[Idx].Mode));
|
||||
if FSlices[Idx].Mode = MODE_WFM then ApplyRXWFMToChan(Chan);
|
||||
if FSlices[Idx].Mode = MODE_DMR then ApplyRXDMRToChan(Chan);
|
||||
if FSlices[Idx].Mode = MODE_FMRAW then ApplyRXRawToChan(Chan);
|
||||
SetRXAShiftRun(Chan, 1);
|
||||
SetRXAShiftFreq(Chan, FSlices[Idx].ShiftHz);
|
||||
RXASetPassband(Chan, ClampToNyquist(FSlices[Idx].FilterLo, DSPRate),
|
||||
@@ -2324,7 +2344,7 @@ begin
|
||||
ApplyANFToChan(Chan, FSlices[Idx].ANFOn);
|
||||
ApplyFMSQToChan(Chan, FSlices[Idx].FMSQOn, FSlices[Idx].FMSQLevel);
|
||||
// Decoder input must not disappear or change scale with speaker volume.
|
||||
if FSlices[Idx].Mode = MODE_DMR then
|
||||
if FSlices[Idx].Mode in [MODE_DMR, MODE_FMRAW] then
|
||||
SetRXAPanelGain1(Chan, 1.0)
|
||||
else
|
||||
SetRXAPanelGain1(Chan, FSlices[Idx].Volume);
|
||||
@@ -2374,7 +2394,7 @@ begin
|
||||
if not (FSlices[s].Active and FSlices[s].Opened) then Continue;
|
||||
if FSlices[s].PanId <> PanId then Continue;
|
||||
// Muting a DMR speaker must not make its decoder lose synchronization.
|
||||
if FSlices[s].Muted and (FSlices[s].Mode <> MODE_DMR) then Continue;
|
||||
if FSlices[s].Muted and not (FSlices[s].Mode in [MODE_DMR, MODE_FMRAW]) then Continue;
|
||||
// Свежая копия входного IQ — fexchange0 портит вход in-place.
|
||||
for i := 0 to N - 1 do
|
||||
begin
|
||||
@@ -2390,12 +2410,12 @@ begin
|
||||
FSliceOutL[k] := FSliceOut[k * 2];
|
||||
FSliceOutR[k] := FSliceOut[k * 2 + 1];
|
||||
end;
|
||||
if FSlices[s].Mode = MODE_DMR then
|
||||
if FSlices[s].Mode in [MODE_DMR, MODE_FMRAW] then
|
||||
begin
|
||||
if Assigned(FOnSliceDemodAudio) then
|
||||
FOnSliceDemodAudio(FSlices[s].Id, FSliceOutL, FSliceOutR,
|
||||
FAudioBufSize);
|
||||
Continue; // never route the raw 4FSK discriminator to the speaker
|
||||
Continue; // decoder-grade signal uses the independent pre-volume route
|
||||
end;
|
||||
if Assigned(FOnSliceAudio) then
|
||||
begin
|
||||
@@ -2616,7 +2636,8 @@ begin
|
||||
SetRXAMode(FSlices[idx].Chan, ModeToWDSP(Mode));
|
||||
if Mode = MODE_WFM then ApplyRXWFMToChan(FSlices[idx].Chan);
|
||||
if Mode = MODE_DMR then ApplyRXDMRToChan(FSlices[idx].Chan);
|
||||
if Mode = MODE_DMR then
|
||||
if Mode = MODE_FMRAW then ApplyRXRawToChan(FSlices[idx].Chan);
|
||||
if Mode in [MODE_DMR, MODE_FMRAW] then
|
||||
SetRXAPanelGain1(FSlices[idx].Chan, 1.0)
|
||||
else
|
||||
SetRXAPanelGain1(FSlices[idx].Chan, FSlices[idx].Volume);
|
||||
@@ -2665,7 +2686,7 @@ begin
|
||||
try
|
||||
idx := FindSliceIdx(Id); if idx < 0 then Exit;
|
||||
FSlices[idx].Volume := Vol;
|
||||
if FSlices[idx].Opened and (FSlices[idx].Mode <> MODE_DMR) then
|
||||
if FSlices[idx].Opened and not (FSlices[idx].Mode in [MODE_DMR, MODE_FMRAW]) then
|
||||
SetRXAPanelGain1(FSlices[idx].Chan, Vol);
|
||||
finally
|
||||
FSliceLock.Leave;
|
||||
@@ -2810,7 +2831,8 @@ begin
|
||||
SetRXAWFMDeviation (RXA_CHAN, DMR_DEVIATION);
|
||||
SetRXAWFMAFFilter (RXA_CHAN, 20.0, 7000.0);
|
||||
SetRXAWFMDeemphRun (RXA_CHAN, 0);
|
||||
end;
|
||||
end
|
||||
else if Mode = MODE_FMRAW then ApplyRXRawToChan(RXA_CHAN);
|
||||
// НЕ вызываем ApplyDefaultFilter — фильтр устанавливается явно из UI
|
||||
end;
|
||||
|
||||
@@ -3046,12 +3068,31 @@ end;
|
||||
procedure TWDSPEngine.SetRXFMDeviation(Deviation: Double);
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
if FMode = MODE_DMR then
|
||||
if FMode = MODE_FMRAW then SetRawFMDeviation(Deviation)
|
||||
else if FMode = MODE_DMR then
|
||||
SetRXAWFMDeviation(RXA_CHAN, Deviation)
|
||||
else
|
||||
SetRXAFMDeviation(RXA_CHAN, Deviation);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetRawFMDeviation(Deviation: Double);
|
||||
var i: Integer;
|
||||
begin
|
||||
FRawFMDeviation := EnsureRange(Deviation, 500.0, 10000.0);
|
||||
if not FInitialized then Exit;
|
||||
if FMode = MODE_FMRAW then SetRXAWFMDeviation(RXA_CHAN, FRawFMDeviation);
|
||||
// One calibration value applies to every active FM RAW decoder output.
|
||||
FSliceLock.Enter;
|
||||
try
|
||||
for i := 0 to MAX_SLICES - 1 do
|
||||
if FSlices[i].Active and FSlices[i].Opened and
|
||||
(FSlices[i].Mode = MODE_FMRAW) then
|
||||
SetRXAWFMDeviation(FSlices[i].Chan, FRawFMDeviation);
|
||||
finally
|
||||
FSliceLock.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetFMSquelch(Enable: Boolean; Level: Integer);
|
||||
var Threshold: Double;
|
||||
begin
|
||||
|
||||
Reference in New Issue
Block a user