mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
fix(dmr): center discriminator eye per sync
This commit is contained in:
+54
-12
@@ -24,6 +24,7 @@ const
|
||||
DMR_INPUT_RATE = 48000;
|
||||
DMR_SYMBOL_RATE = 4800;
|
||||
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_BURST_DIBITS = 144;
|
||||
DMR_SYNC_END_DIBIT = 89; // sync occupies burst dibits 66..89
|
||||
@@ -100,7 +101,7 @@ type
|
||||
FReadPos, FWritePos: Integer;
|
||||
FDropped: QWord;
|
||||
|
||||
FSampleWindow: array[0..DMR_SAMPLES_PER_SYM - 1] of Double;
|
||||
FSampleWindow: array[0..DMR_INTEGRATOR_SAMPLES - 1] of Double;
|
||||
FWindowPos, FWindowCount: Integer;
|
||||
FWindowSum: Double;
|
||||
FSampleNo: QWord;
|
||||
@@ -117,6 +118,9 @@ type
|
||||
FMagSum: array[0..DMR_SAMPLES_PER_SYM - 1] of Double;
|
||||
FBestSyncQuality: Double;
|
||||
FFixedOuterLevel: Double;
|
||||
FFixedPositiveOuter: Double;
|
||||
FFixedNegativeOuter: Double;
|
||||
FFixedCenter: Double;
|
||||
FTrackPhase: Integer;
|
||||
FCurrentBurst: TDMRDibitBurst;
|
||||
FCurrentBurstPos: Integer;
|
||||
@@ -155,6 +159,7 @@ type
|
||||
function Digitize(Phase: Integer; Symbol: Double): Byte;
|
||||
function SliceSymbol(Symbol, OuterLevel: Double): Byte;
|
||||
function SyncEyeOpening(Phase: Integer): Double;
|
||||
procedure CalibrateSyncLevels(Phase: Integer);
|
||||
procedure StartBurstTracking(Phase: Integer);
|
||||
procedure CompleteBurst;
|
||||
procedure DecodeVoiceBurst;
|
||||
@@ -315,6 +320,9 @@ begin
|
||||
FillChar(FMagSum, SizeOf(FMagSum), 0);
|
||||
FBestSyncQuality := 0;
|
||||
FFixedOuterLevel := 0;
|
||||
FFixedPositiveOuter := 0;
|
||||
FFixedNegativeOuter := 0;
|
||||
FFixedCenter := 0;
|
||||
FillChar(FCurrentBurst, SizeOf(FCurrentBurst), 0);
|
||||
FillChar(FLastBurst, SizeOf(FLastBurst), 0);
|
||||
FillChar(FLastBurstInfo, SizeOf(FLastBurstInfo), 0);
|
||||
@@ -427,15 +435,15 @@ begin
|
||||
FSignalMean := FSignalMean + 0.001 * (S - FSignalMean);
|
||||
FWindowSum := FWindowSum - FSampleWindow[FWindowPos] + S;
|
||||
FSampleWindow[FWindowPos] := S;
|
||||
FWindowPos := (FWindowPos + 1) mod DMR_SAMPLES_PER_SYM;
|
||||
if FWindowCount < DMR_SAMPLES_PER_SYM then Inc(FWindowCount);
|
||||
FWindowPos := (FWindowPos + 1) mod DMR_INTEGRATOR_SAMPLES;
|
||||
if FWindowCount < DMR_INTEGRATOR_SAMPLES then Inc(FWindowCount);
|
||||
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
|
||||
// symbol every ten input samples; the correct phase produces exact sync.
|
||||
Phase := Integer(FSampleNo mod DMR_SAMPLES_PER_SYM);
|
||||
Sym := FWindowSum / DMR_SAMPLES_PER_SYM;
|
||||
Sym := FWindowSum / DMR_INTEGRATOR_SAMPLES;
|
||||
ProcessSymbol(Phase, Sym);
|
||||
end;
|
||||
|
||||
@@ -580,6 +588,9 @@ begin
|
||||
FTrackPhase := -1;
|
||||
FCurrentBurstPos := 0;
|
||||
FFixedOuterLevel := 0;
|
||||
FFixedPositiveOuter := 0;
|
||||
FFixedNegativeOuter := 0;
|
||||
FFixedCenter := 0;
|
||||
FMobileActiveBurst := True;
|
||||
DMRMbeReset(FMbe);
|
||||
end;
|
||||
@@ -589,7 +600,7 @@ var
|
||||
A: Double;
|
||||
begin
|
||||
if (Phase = FTrackPhase) and (FFixedOuterLevel > 1.0e-9) then
|
||||
Exit(SliceSymbol(Symbol, FFixedOuterLevel));
|
||||
Exit(SliceSymbol(Symbol - FFixedCenter, FFixedOuterLevel));
|
||||
|
||||
A := Abs(Symbol);
|
||||
if FOuterLevel[Phase] <= 1.0e-9 then FOuterLevel[Phase] := A
|
||||
@@ -605,7 +616,7 @@ var
|
||||
A, Threshold: Double;
|
||||
begin
|
||||
A := Abs(Symbol);
|
||||
Threshold := (2.0 / 3.0) * OuterLevel;
|
||||
Threshold := 0.645 * OuterLevel;
|
||||
if Symbol >= 0 then
|
||||
begin
|
||||
if A >= Threshold then Result := 1 else Result := 0; // +3 / +1
|
||||
@@ -625,6 +636,39 @@ begin
|
||||
Result := Min(Result, FMagHistory[Phase, i]);
|
||||
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);
|
||||
var
|
||||
i, P: Integer;
|
||||
@@ -636,7 +680,7 @@ begin
|
||||
begin
|
||||
// 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],
|
||||
FCurrentBurst[i] := SliceSymbol(FSymbolHistory[Phase, P] - FFixedCenter,
|
||||
FFixedOuterLevel);
|
||||
P := (P + 1) mod (DMR_SYNC_END_DIBIT + 1);
|
||||
end;
|
||||
@@ -755,8 +799,7 @@ begin
|
||||
FBestSyncQuality := SyncEyeOpening(Phase);
|
||||
FSyncKind := Kind;
|
||||
FInverted := Inverted;
|
||||
FFixedOuterLevel := FMagSum[Phase] / 24.0;
|
||||
FOuterLevel[Phase] := FFixedOuterLevel;
|
||||
CalibrateSyncLevels(Phase);
|
||||
StartBurstTracking(Phase);
|
||||
end;
|
||||
Exit;
|
||||
@@ -767,8 +810,7 @@ begin
|
||||
FLastSyncTick := NowTick;
|
||||
FLastSyncSample := FSampleNo;
|
||||
FBestSyncQuality := SyncEyeOpening(Phase);
|
||||
FFixedOuterLevel := FMagSum[Phase] / 24.0;
|
||||
FOuterLevel[Phase] := FFixedOuterLevel;
|
||||
CalibrateSyncLevels(Phase);
|
||||
if Kind in [dskMSData, dskMSVoice] then FMobileActiveBurst := True;
|
||||
StartBurstTracking(Phase);
|
||||
finally
|
||||
|
||||
@@ -67,6 +67,8 @@ begin
|
||||
end;
|
||||
|
||||
procedure TestBurst(D: TDMRDecoder);
|
||||
const
|
||||
DC_OFFSET = 0.12;
|
||||
var
|
||||
Want, Got: array[0..DMR_BURST_DIBITS - 1] of Byte;
|
||||
L, R: array of Single;
|
||||
@@ -84,17 +86,17 @@ begin
|
||||
SetLength(R, Length(L));
|
||||
// One complete preceding symbol mirrors a continuous stream and fills the
|
||||
// 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;
|
||||
for j := 0 to p - 1 do begin L[j] := V; R[j] := V; end;
|
||||
for i := 0 to High(Want) do
|
||||
begin
|
||||
case Want[i] of
|
||||
0: V := 0.23;
|
||||
1: V := 0.70;
|
||||
2: V := -0.23;
|
||||
0: V := 0.23 + DC_OFFSET;
|
||||
1: V := 0.70 + DC_OFFSET;
|
||||
2: V := -0.23 + DC_OFFSET;
|
||||
else
|
||||
V := -0.70;
|
||||
V := -0.70 + DC_OFFSET;
|
||||
end;
|
||||
for j := 0 to DMR_SAMPLES_PER_SYM - 1 do
|
||||
begin
|
||||
|
||||
Reference in New Issue
Block a user