fix(dmr): calibrate slicer from sync symbols

This commit is contained in:
2026-07-14 19:38:56 +03:00
parent a19c82a214
commit a633bf57cd
+45 -7
View File
@@ -108,12 +108,15 @@ type
FOuterLevel: array[0..DMR_SAMPLES_PER_SYM - 1] of Double; FOuterLevel: array[0..DMR_SAMPLES_PER_SYM - 1] of Double;
FDibitHistory: array[0..DMR_SAMPLES_PER_SYM - 1, FDibitHistory: array[0..DMR_SAMPLES_PER_SYM - 1,
0..DMR_SYNC_END_DIBIT] of Byte; 0..DMR_SYNC_END_DIBIT] of Byte;
FSymbolHistory: array[0..DMR_SAMPLES_PER_SYM - 1,
0..DMR_SYNC_END_DIBIT] of Double;
FHistoryPos: array[0..DMR_SAMPLES_PER_SYM - 1] of Integer; FHistoryPos: array[0..DMR_SAMPLES_PER_SYM - 1] of Integer;
FHistoryCount: array[0..DMR_SAMPLES_PER_SYM - 1] of Integer; FHistoryCount: array[0..DMR_SAMPLES_PER_SYM - 1] of Integer;
FMagHistory: array[0..DMR_SAMPLES_PER_SYM - 1, 0..23] of Double; FMagHistory: array[0..DMR_SAMPLES_PER_SYM - 1, 0..23] of Double;
FMagPos: array[0..DMR_SAMPLES_PER_SYM - 1] of Integer; FMagPos: array[0..DMR_SAMPLES_PER_SYM - 1] of Integer;
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;
FTrackPhase: Integer; FTrackPhase: Integer;
FCurrentBurst: TDMRDibitBurst; FCurrentBurst: TDMRDibitBurst;
FCurrentBurstPos: Integer; FCurrentBurstPos: Integer;
@@ -150,6 +153,8 @@ type
procedure NoteSync(Phase: Integer; Kind: TDMRSyncKind; Inverted: Boolean); procedure NoteSync(Phase: Integer; Kind: TDMRSyncKind; Inverted: Boolean);
procedure LoseSync; procedure LoseSync;
function Digitize(Phase: Integer; Symbol: Double): Byte; function Digitize(Phase: Integer; Symbol: Double): Byte;
function SliceSymbol(Symbol, OuterLevel: Double): Byte;
function SyncEyeOpening(Phase: Integer): Double;
procedure StartBurstTracking(Phase: Integer); procedure StartBurstTracking(Phase: Integer);
procedure CompleteBurst; procedure CompleteBurst;
procedure DecodeVoiceBurst; procedure DecodeVoiceBurst;
@@ -302,12 +307,14 @@ begin
FillChar(FSyncShift, SizeOf(FSyncShift), 0); FillChar(FSyncShift, SizeOf(FSyncShift), 0);
FillChar(FOuterLevel, SizeOf(FOuterLevel), 0); FillChar(FOuterLevel, SizeOf(FOuterLevel), 0);
FillChar(FDibitHistory, SizeOf(FDibitHistory), 0); FillChar(FDibitHistory, SizeOf(FDibitHistory), 0);
FillChar(FSymbolHistory, SizeOf(FSymbolHistory), 0);
FillChar(FHistoryPos, SizeOf(FHistoryPos), 0); FillChar(FHistoryPos, SizeOf(FHistoryPos), 0);
FillChar(FHistoryCount, SizeOf(FHistoryCount), 0); FillChar(FHistoryCount, SizeOf(FHistoryCount), 0);
FillChar(FMagHistory, SizeOf(FMagHistory), 0); FillChar(FMagHistory, SizeOf(FMagHistory), 0);
FillChar(FMagPos, SizeOf(FMagPos), 0); FillChar(FMagPos, SizeOf(FMagPos), 0);
FillChar(FMagSum, SizeOf(FMagSum), 0); FillChar(FMagSum, SizeOf(FMagSum), 0);
FBestSyncQuality := 0; FBestSyncQuality := 0;
FFixedOuterLevel := 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);
@@ -456,6 +463,7 @@ begin
// Keep the 90 dibits ending at sync. Once the timing phase is selected this // Keep the 90 dibits ending at sync. Once the timing phase is selected this
// supplies burst positions 0..89 without waiting for another frame. // supplies burst positions 0..89 without waiting for another frame.
FDibitHistory[Phase, FHistoryPos[Phase]] := Dibit; FDibitHistory[Phase, FHistoryPos[Phase]] := Dibit;
FSymbolHistory[Phase, FHistoryPos[Phase]] := V;
FHistoryPos[Phase] := (FHistoryPos[Phase] + 1) mod (DMR_SYNC_END_DIBIT + 1); FHistoryPos[Phase] := (FHistoryPos[Phase] + 1) mod (DMR_SYNC_END_DIBIT + 1);
if FHistoryCount[Phase] < DMR_SYNC_END_DIBIT + 1 then Inc(FHistoryCount[Phase]); if FHistoryCount[Phase] < DMR_SYNC_END_DIBIT + 1 then Inc(FHistoryCount[Phase]);
@@ -571,21 +579,33 @@ begin
end; end;
FTrackPhase := -1; FTrackPhase := -1;
FCurrentBurstPos := 0; FCurrentBurstPos := 0;
FFixedOuterLevel := 0;
FMobileActiveBurst := True; FMobileActiveBurst := True;
DMRMbeReset(FMbe); DMRMbeReset(FMbe);
end; end;
function TDMRDecoder.Digitize(Phase: Integer; Symbol: Double): Byte; function TDMRDecoder.Digitize(Phase: Integer; Symbol: Double): Byte;
var var
A, Threshold: Double; A: Double;
begin begin
if (Phase = FTrackPhase) and (FFixedOuterLevel > 1.0e-9) then
Exit(SliceSymbol(Symbol, 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
else if A > FOuterLevel[Phase] then else if A > FOuterLevel[Phase] then
FOuterLevel[Phase] := A FOuterLevel[Phase] := A
else else
FOuterLevel[Phase] := FOuterLevel[Phase] + 0.001 * (A - FOuterLevel[Phase]); FOuterLevel[Phase] := FOuterLevel[Phase] + 0.001 * (A - FOuterLevel[Phase]);
Threshold := 0.625 * FOuterLevel[Phase]; Result := SliceSymbol(Symbol, FOuterLevel[Phase]);
end;
function TDMRDecoder.SliceSymbol(Symbol, OuterLevel: Double): Byte;
var
A, Threshold: Double;
begin
A := Abs(Symbol);
Threshold := (2.0 / 3.0) * 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
@@ -596,6 +616,15 @@ begin
end; end;
end; end;
function TDMRDecoder.SyncEyeOpening(Phase: Integer): Double;
var
i: Integer;
begin
Result := 1.0e300;
for i := 0 to 23 do
Result := Min(Result, FMagHistory[Phase, i]);
end;
procedure TDMRDecoder.StartBurstTracking(Phase: Integer); procedure TDMRDecoder.StartBurstTracking(Phase: Integer);
var var
i, P: Integer; i, P: Integer;
@@ -605,7 +634,10 @@ begin
P := FHistoryPos[Phase]; // oldest item; ring is exactly 90 dibits full P := FHistoryPos[Phase]; // oldest item; ring is exactly 90 dibits full
for i := 0 to DMR_SYNC_END_DIBIT do for i := 0 to DMR_SYNC_END_DIBIT do
begin begin
FCurrentBurst[i] := FDibitHistory[Phase, P]; // 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.
FCurrentBurst[i] := SliceSymbol(FSymbolHistory[Phase, P],
FFixedOuterLevel);
P := (P + 1) mod (DMR_SYNC_END_DIBIT + 1); P := (P + 1) mod (DMR_SYNC_END_DIBIT + 1);
end; end;
FCurrentBurstPos := DMR_SYNC_END_DIBIT + 1; FCurrentBurstPos := DMR_SYNC_END_DIBIT + 1;
@@ -718,11 +750,13 @@ begin
if (FLastSyncSample <> 0) and if (FLastSyncSample <> 0) and
(FSampleNo - FLastSyncSample <= DMR_SAMPLES_PER_SYM * 2) then (FSampleNo - FLastSyncSample <= DMR_SAMPLES_PER_SYM * 2) then
begin begin
if FMagSum[Phase] > FBestSyncQuality then if SyncEyeOpening(Phase) > FBestSyncQuality then
begin begin
FBestSyncQuality := FMagSum[Phase]; FBestSyncQuality := SyncEyeOpening(Phase);
FSyncKind := Kind; FSyncKind := Kind;
FInverted := Inverted; FInverted := Inverted;
FFixedOuterLevel := FMagSum[Phase] / 24.0;
FOuterLevel[Phase] := FFixedOuterLevel;
StartBurstTracking(Phase); StartBurstTracking(Phase);
end; end;
Exit; Exit;
@@ -732,7 +766,9 @@ begin
Inc(FSyncCount); Inc(FSyncCount);
FLastSyncTick := NowTick; FLastSyncTick := NowTick;
FLastSyncSample := FSampleNo; FLastSyncSample := FSampleNo;
FBestSyncQuality := FMagSum[Phase]; FBestSyncQuality := SyncEyeOpening(Phase);
FFixedOuterLevel := FMagSum[Phase] / 24.0;
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
@@ -795,7 +831,9 @@ begin
S.BestSyncDistance := FBestSyncDistance; S.BestSyncDistance := FBestSyncDistance;
S.BestSyncPhase := FBestSyncPhase; S.BestSyncPhase := FBestSyncPhase;
S.BestCandidateKind := FBestCandidateKind; S.BestCandidateKind := FBestCandidateKind;
if FBestSyncPhase >= 0 then if FFixedOuterLevel > 0 then
S.SymbolOuterLevel := FFixedOuterLevel
else if FBestSyncPhase >= 0 then
S.SymbolOuterLevel := FOuterLevel[FBestSyncPhase]; S.SymbolOuterLevel := FOuterLevel[FBestSyncPhase];
S.SignalMean := FSignalMean; S.SignalMean := FSignalMean;
finally finally