fix(dmr): choose timing phase by sync variance

This commit is contained in:
2026-07-14 19:56:36 +03:00
parent 22505d4df7
commit 9e2f8c7743
+46 -10
View File
@@ -158,7 +158,7 @@ type
procedure LoseSync;
function Digitize(Phase: Integer; Symbol: Double): Byte;
function SliceSymbol(Symbol, OuterLevel: Double): Byte;
function SyncEyeOpening(Phase: Integer): Double;
function SyncPhaseQuality(Phase: Integer): Double;
procedure CalibrateSyncLevels(Phase: Integer);
procedure StartBurstTracking(Phase: Integer);
procedure CompleteBurst;
@@ -627,13 +627,49 @@ begin
end;
end;
function TDMRDecoder.SyncEyeOpening(Phase: Integer): Double;
function TDMRDecoder.SyncPhaseQuality(Phase: Integer): Double;
var
i: Integer;
i, P, PositiveCount, NegativeCount: Integer;
V, PositiveMean, NegativeMean, ErrorSum, Scale: Double;
begin
Result := 1.0e300;
PositiveMean := 0;
NegativeMean := 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
Result := Min(Result, FMagHistory[Phase, i]);
begin
V := FSymbolHistory[Phase, P];
if V >= 0 then
begin
PositiveMean := PositiveMean + V;
Inc(PositiveCount);
end
else
begin
NegativeMean := NegativeMean - V;
Inc(NegativeCount);
end;
P := (P + 1) mod (DMR_SYNC_END_DIBIT + 1);
end;
if (PositiveCount = 0) or (NegativeCount = 0) then Exit(-1.0e300);
PositiveMean := PositiveMean / PositiveCount;
NegativeMean := NegativeMean / NegativeCount;
ErrorSum := 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 ErrorSum := ErrorSum + Sqr(V - PositiveMean)
else ErrorSum := ErrorSum + Sqr((-V) - NegativeMean);
P := (P + 1) mod (DMR_SYNC_END_DIBIT + 1);
end;
Scale := Sqr(0.5 * (PositiveMean + NegativeMean));
if Scale <= 1.0e-18 then Exit(-1.0e300);
// Higher is better: zero denotes perfectly compact outer-level clusters.
Result := -ErrorSum / (24.0 * Scale);
end;
procedure TDMRDecoder.CalibrateSyncLevels(Phase: Integer);
@@ -789,14 +825,14 @@ begin
FStatusLock.Enter;
try
// Adjacent timing hypotheses recognize the same physical burst within one
// sample period. Keep the phase with the largest 24-symbol eye opening,
// not merely the first phase that happened to match the sign pattern.
// sample period. Keep the phase whose known positive/negative sync levels
// form the tightest clusters, not merely the first sign-pattern match.
if (FLastSyncSample <> 0) and
(FSampleNo - FLastSyncSample <= DMR_SAMPLES_PER_SYM * 2) then
begin
if SyncEyeOpening(Phase) > FBestSyncQuality then
if SyncPhaseQuality(Phase) > FBestSyncQuality then
begin
FBestSyncQuality := SyncEyeOpening(Phase);
FBestSyncQuality := SyncPhaseQuality(Phase);
FSyncKind := Kind;
FInverted := Inverted;
CalibrateSyncLevels(Phase);
@@ -809,7 +845,7 @@ begin
Inc(FSyncCount);
FLastSyncTick := NowTick;
FLastSyncSample := FSampleNo;
FBestSyncQuality := SyncEyeOpening(Phase);
FBestSyncQuality := SyncPhaseQuality(Phase);
CalibrateSyncLevels(Phase);
if Kind in [dskMSData, dskMSVoice] then FMobileActiveBurst := True;
StartBurstTracking(Phase);