fix(dmr): center discriminator eye per sync

This commit is contained in:
2026-07-14 19:47:55 +03:00
parent a633bf57cd
commit 22505d4df7
2 changed files with 61 additions and 17 deletions
+54 -12
View File
@@ -24,6 +24,7 @@ const
DMR_INPUT_RATE = 48000; DMR_INPUT_RATE = 48000;
DMR_SYMBOL_RATE = 4800; DMR_SYMBOL_RATE = 4800;
DMR_SAMPLES_PER_SYM = DMR_INPUT_RATE div DMR_SYMBOL_RATE; DMR_SAMPLES_PER_SYM = DMR_INPUT_RATE div DMR_SYMBOL_RATE;
DMR_INTEGRATOR_SAMPLES = 8;
DMR_RING_SIZE = 65536; // >1.3 s at 48 kHz, power of two DMR_RING_SIZE = 65536; // >1.3 s at 48 kHz, power of two
DMR_BURST_DIBITS = 144; DMR_BURST_DIBITS = 144;
DMR_SYNC_END_DIBIT = 89; // sync occupies burst dibits 66..89 DMR_SYNC_END_DIBIT = 89; // sync occupies burst dibits 66..89
@@ -100,7 +101,7 @@ type
FReadPos, FWritePos: Integer; FReadPos, FWritePos: Integer;
FDropped: QWord; FDropped: QWord;
FSampleWindow: array[0..DMR_SAMPLES_PER_SYM - 1] of Double; FSampleWindow: array[0..DMR_INTEGRATOR_SAMPLES - 1] of Double;
FWindowPos, FWindowCount: Integer; FWindowPos, FWindowCount: Integer;
FWindowSum: Double; FWindowSum: Double;
FSampleNo: QWord; FSampleNo: QWord;
@@ -117,6 +118,9 @@ type
FMagSum: array[0..DMR_SAMPLES_PER_SYM - 1] of Double; FMagSum: array[0..DMR_SAMPLES_PER_SYM - 1] of Double;
FBestSyncQuality: Double; FBestSyncQuality: Double;
FFixedOuterLevel: Double; FFixedOuterLevel: Double;
FFixedPositiveOuter: Double;
FFixedNegativeOuter: Double;
FFixedCenter: Double;
FTrackPhase: Integer; FTrackPhase: Integer;
FCurrentBurst: TDMRDibitBurst; FCurrentBurst: TDMRDibitBurst;
FCurrentBurstPos: Integer; FCurrentBurstPos: Integer;
@@ -155,6 +159,7 @@ type
function Digitize(Phase: Integer; Symbol: Double): Byte; function Digitize(Phase: Integer; Symbol: Double): Byte;
function SliceSymbol(Symbol, OuterLevel: Double): Byte; function SliceSymbol(Symbol, OuterLevel: Double): Byte;
function SyncEyeOpening(Phase: Integer): Double; function SyncEyeOpening(Phase: Integer): Double;
procedure CalibrateSyncLevels(Phase: Integer);
procedure StartBurstTracking(Phase: Integer); procedure StartBurstTracking(Phase: Integer);
procedure CompleteBurst; procedure CompleteBurst;
procedure DecodeVoiceBurst; procedure DecodeVoiceBurst;
@@ -315,6 +320,9 @@ begin
FillChar(FMagSum, SizeOf(FMagSum), 0); FillChar(FMagSum, SizeOf(FMagSum), 0);
FBestSyncQuality := 0; FBestSyncQuality := 0;
FFixedOuterLevel := 0; FFixedOuterLevel := 0;
FFixedPositiveOuter := 0;
FFixedNegativeOuter := 0;
FFixedCenter := 0;
FillChar(FCurrentBurst, SizeOf(FCurrentBurst), 0); FillChar(FCurrentBurst, SizeOf(FCurrentBurst), 0);
FillChar(FLastBurst, SizeOf(FLastBurst), 0); FillChar(FLastBurst, SizeOf(FLastBurst), 0);
FillChar(FLastBurstInfo, SizeOf(FLastBurstInfo), 0); FillChar(FLastBurstInfo, SizeOf(FLastBurstInfo), 0);
@@ -427,15 +435,15 @@ begin
FSignalMean := FSignalMean + 0.001 * (S - FSignalMean); FSignalMean := FSignalMean + 0.001 * (S - FSignalMean);
FWindowSum := FWindowSum - FSampleWindow[FWindowPos] + S; FWindowSum := FWindowSum - FSampleWindow[FWindowPos] + S;
FSampleWindow[FWindowPos] := S; FSampleWindow[FWindowPos] := S;
FWindowPos := (FWindowPos + 1) mod DMR_SAMPLES_PER_SYM; FWindowPos := (FWindowPos + 1) mod DMR_INTEGRATOR_SAMPLES;
if FWindowCount < DMR_SAMPLES_PER_SYM then Inc(FWindowCount); if FWindowCount < DMR_INTEGRATOR_SAMPLES then Inc(FWindowCount);
Inc(FSampleNo); Inc(FSampleNo);
if FWindowCount < DMR_SAMPLES_PER_SYM then Exit; if FWindowCount < DMR_INTEGRATOR_SAMPLES then Exit;
// Ten interleaved timing hypotheses. Each receives one boxcar-integrated // Ten interleaved timing hypotheses. Each receives one boxcar-integrated
// symbol every ten input samples; the correct phase produces exact sync. // symbol every ten input samples; the correct phase produces exact sync.
Phase := Integer(FSampleNo mod DMR_SAMPLES_PER_SYM); Phase := Integer(FSampleNo mod DMR_SAMPLES_PER_SYM);
Sym := FWindowSum / DMR_SAMPLES_PER_SYM; Sym := FWindowSum / DMR_INTEGRATOR_SAMPLES;
ProcessSymbol(Phase, Sym); ProcessSymbol(Phase, Sym);
end; end;
@@ -580,6 +588,9 @@ begin
FTrackPhase := -1; FTrackPhase := -1;
FCurrentBurstPos := 0; FCurrentBurstPos := 0;
FFixedOuterLevel := 0; FFixedOuterLevel := 0;
FFixedPositiveOuter := 0;
FFixedNegativeOuter := 0;
FFixedCenter := 0;
FMobileActiveBurst := True; FMobileActiveBurst := True;
DMRMbeReset(FMbe); DMRMbeReset(FMbe);
end; end;
@@ -589,7 +600,7 @@ var
A: Double; A: Double;
begin begin
if (Phase = FTrackPhase) and (FFixedOuterLevel > 1.0e-9) then if (Phase = FTrackPhase) and (FFixedOuterLevel > 1.0e-9) then
Exit(SliceSymbol(Symbol, FFixedOuterLevel)); Exit(SliceSymbol(Symbol - FFixedCenter, FFixedOuterLevel));
A := Abs(Symbol); A := Abs(Symbol);
if FOuterLevel[Phase] <= 1.0e-9 then FOuterLevel[Phase] := A if FOuterLevel[Phase] <= 1.0e-9 then FOuterLevel[Phase] := A
@@ -605,7 +616,7 @@ var
A, Threshold: Double; A, Threshold: Double;
begin begin
A := Abs(Symbol); A := Abs(Symbol);
Threshold := (2.0 / 3.0) * OuterLevel; Threshold := 0.645 * OuterLevel;
if Symbol >= 0 then if Symbol >= 0 then
begin begin
if A >= Threshold then Result := 1 else Result := 0; // +3 / +1 if A >= Threshold then Result := 1 else Result := 0; // +3 / +1
@@ -625,6 +636,39 @@ begin
Result := Min(Result, FMagHistory[Phase, i]); Result := Min(Result, FMagHistory[Phase, i]);
end; end;
procedure TDMRDecoder.CalibrateSyncLevels(Phase: Integer);
var
i, P, PositiveCount, NegativeCount: Integer;
V, PositiveSum, NegativeSum: Double;
begin
PositiveSum := 0;
NegativeSum := 0;
PositiveCount := 0;
NegativeCount := 0;
P := (FHistoryPos[Phase] + DMR_SYNC_END_DIBIT + 1 - 24) mod
(DMR_SYNC_END_DIBIT + 1);
for i := 0 to 23 do
begin
V := FSymbolHistory[Phase, P];
if V >= 0 then
begin
PositiveSum := PositiveSum + V;
Inc(PositiveCount);
end
else
begin
NegativeSum := NegativeSum - V;
Inc(NegativeCount);
end;
P := (P + 1) mod (DMR_SYNC_END_DIBIT + 1);
end;
if PositiveCount > 0 then FFixedPositiveOuter := PositiveSum / PositiveCount;
if NegativeCount > 0 then FFixedNegativeOuter := NegativeSum / NegativeCount;
FFixedCenter := 0.5 * (FFixedPositiveOuter - FFixedNegativeOuter);
FFixedOuterLevel := 0.5 * (FFixedPositiveOuter + FFixedNegativeOuter);
FOuterLevel[Phase] := FFixedOuterLevel;
end;
procedure TDMRDecoder.StartBurstTracking(Phase: Integer); procedure TDMRDecoder.StartBurstTracking(Phase: Integer);
var var
i, P: Integer; i, P: Integer;
@@ -636,7 +680,7 @@ begin
begin begin
// All 24 DMR sync dibits are known outer levels. Once they calibrate the // All 24 DMR sync dibits are known outer levels. Once they calibrate the
// eye, re-slice the already buffered pre-sync payload with the same level. // eye, re-slice the already buffered pre-sync payload with the same level.
FCurrentBurst[i] := SliceSymbol(FSymbolHistory[Phase, P], FCurrentBurst[i] := SliceSymbol(FSymbolHistory[Phase, P] - FFixedCenter,
FFixedOuterLevel); FFixedOuterLevel);
P := (P + 1) mod (DMR_SYNC_END_DIBIT + 1); P := (P + 1) mod (DMR_SYNC_END_DIBIT + 1);
end; end;
@@ -755,8 +799,7 @@ begin
FBestSyncQuality := SyncEyeOpening(Phase); FBestSyncQuality := SyncEyeOpening(Phase);
FSyncKind := Kind; FSyncKind := Kind;
FInverted := Inverted; FInverted := Inverted;
FFixedOuterLevel := FMagSum[Phase] / 24.0; CalibrateSyncLevels(Phase);
FOuterLevel[Phase] := FFixedOuterLevel;
StartBurstTracking(Phase); StartBurstTracking(Phase);
end; end;
Exit; Exit;
@@ -767,8 +810,7 @@ begin
FLastSyncTick := NowTick; FLastSyncTick := NowTick;
FLastSyncSample := FSampleNo; FLastSyncSample := FSampleNo;
FBestSyncQuality := SyncEyeOpening(Phase); FBestSyncQuality := SyncEyeOpening(Phase);
FFixedOuterLevel := FMagSum[Phase] / 24.0; CalibrateSyncLevels(Phase);
FOuterLevel[Phase] := FFixedOuterLevel;
if Kind in [dskMSData, dskMSVoice] then FMobileActiveBurst := True; if Kind in [dskMSData, dskMSVoice] then FMobileActiveBurst := True;
StartBurstTracking(Phase); StartBurstTracking(Phase);
finally finally
+7 -5
View File
@@ -67,6 +67,8 @@ begin
end; end;
procedure TestBurst(D: TDMRDecoder); procedure TestBurst(D: TDMRDecoder);
const
DC_OFFSET = 0.12;
var var
Want, Got: array[0..DMR_BURST_DIBITS - 1] of Byte; Want, Got: array[0..DMR_BURST_DIBITS - 1] of Byte;
L, R: array of Single; L, R: array of Single;
@@ -84,17 +86,17 @@ begin
SetLength(R, Length(L)); SetLength(R, Length(L));
// One complete preceding symbol mirrors a continuous stream and fills the // One complete preceding symbol mirrors a continuous stream and fills the
// 90-dibit history even for the latest timing hypothesis. // 90-dibit history even for the latest timing hypothesis.
V := 0.70; // Want[0] = 1 V := 0.70 + DC_OFFSET; // Want[0] = 1
p := 3 + DMR_SAMPLES_PER_SYM; p := 3 + DMR_SAMPLES_PER_SYM;
for j := 0 to p - 1 do begin L[j] := V; R[j] := V; end; for j := 0 to p - 1 do begin L[j] := V; R[j] := V; end;
for i := 0 to High(Want) do for i := 0 to High(Want) do
begin begin
case Want[i] of case Want[i] of
0: V := 0.23; 0: V := 0.23 + DC_OFFSET;
1: V := 0.70; 1: V := 0.70 + DC_OFFSET;
2: V := -0.23; 2: V := -0.23 + DC_OFFSET;
else else
V := -0.70; V := -0.70 + DC_OFFSET;
end; end;
for j := 0 to DMR_SAMPLES_PER_SYM - 1 do for j := 0 to DMR_SAMPLES_PER_SYM - 1 do
begin begin