diff --git a/DMRDecoder.pas b/DMRDecoder.pas index fce392a..b73db2d 100644 --- a/DMRDecoder.pas +++ b/DMRDecoder.pas @@ -108,12 +108,15 @@ type FOuterLevel: array[0..DMR_SAMPLES_PER_SYM - 1] of Double; FDibitHistory: array[0..DMR_SAMPLES_PER_SYM - 1, 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; FHistoryCount: array[0..DMR_SAMPLES_PER_SYM - 1] of Integer; FMagHistory: array[0..DMR_SAMPLES_PER_SYM - 1, 0..23] of Double; FMagPos: array[0..DMR_SAMPLES_PER_SYM - 1] of Integer; FMagSum: array[0..DMR_SAMPLES_PER_SYM - 1] of Double; FBestSyncQuality: Double; + FFixedOuterLevel: Double; FTrackPhase: Integer; FCurrentBurst: TDMRDibitBurst; FCurrentBurstPos: Integer; @@ -150,6 +153,8 @@ type procedure NoteSync(Phase: Integer; Kind: TDMRSyncKind; Inverted: Boolean); procedure LoseSync; function Digitize(Phase: Integer; Symbol: Double): Byte; + function SliceSymbol(Symbol, OuterLevel: Double): Byte; + function SyncEyeOpening(Phase: Integer): Double; procedure StartBurstTracking(Phase: Integer); procedure CompleteBurst; procedure DecodeVoiceBurst; @@ -302,12 +307,14 @@ begin FillChar(FSyncShift, SizeOf(FSyncShift), 0); FillChar(FOuterLevel, SizeOf(FOuterLevel), 0); FillChar(FDibitHistory, SizeOf(FDibitHistory), 0); + FillChar(FSymbolHistory, SizeOf(FSymbolHistory), 0); FillChar(FHistoryPos, SizeOf(FHistoryPos), 0); FillChar(FHistoryCount, SizeOf(FHistoryCount), 0); FillChar(FMagHistory, SizeOf(FMagHistory), 0); FillChar(FMagPos, SizeOf(FMagPos), 0); FillChar(FMagSum, SizeOf(FMagSum), 0); FBestSyncQuality := 0; + FFixedOuterLevel := 0; FillChar(FCurrentBurst, SizeOf(FCurrentBurst), 0); FillChar(FLastBurst, SizeOf(FLastBurst), 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 // supplies burst positions 0..89 without waiting for another frame. FDibitHistory[Phase, FHistoryPos[Phase]] := Dibit; + FSymbolHistory[Phase, FHistoryPos[Phase]] := V; FHistoryPos[Phase] := (FHistoryPos[Phase] + 1) mod (DMR_SYNC_END_DIBIT + 1); if FHistoryCount[Phase] < DMR_SYNC_END_DIBIT + 1 then Inc(FHistoryCount[Phase]); @@ -571,21 +579,33 @@ begin end; FTrackPhase := -1; FCurrentBurstPos := 0; + FFixedOuterLevel := 0; FMobileActiveBurst := True; DMRMbeReset(FMbe); end; function TDMRDecoder.Digitize(Phase: Integer; Symbol: Double): Byte; var - A, Threshold: Double; + A: Double; begin + if (Phase = FTrackPhase) and (FFixedOuterLevel > 1.0e-9) then + Exit(SliceSymbol(Symbol, FFixedOuterLevel)); + A := Abs(Symbol); if FOuterLevel[Phase] <= 1.0e-9 then FOuterLevel[Phase] := A else if A > FOuterLevel[Phase] then FOuterLevel[Phase] := A else 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 begin if A >= Threshold then Result := 1 else Result := 0; // +3 / +1 @@ -596,6 +616,15 @@ begin 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); var i, P: Integer; @@ -605,7 +634,10 @@ begin P := FHistoryPos[Phase]; // oldest item; ring is exactly 90 dibits full for i := 0 to DMR_SYNC_END_DIBIT do 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); end; FCurrentBurstPos := DMR_SYNC_END_DIBIT + 1; @@ -718,11 +750,13 @@ begin if (FLastSyncSample <> 0) and (FSampleNo - FLastSyncSample <= DMR_SAMPLES_PER_SYM * 2) then begin - if FMagSum[Phase] > FBestSyncQuality then + if SyncEyeOpening(Phase) > FBestSyncQuality then begin - FBestSyncQuality := FMagSum[Phase]; + FBestSyncQuality := SyncEyeOpening(Phase); FSyncKind := Kind; FInverted := Inverted; + FFixedOuterLevel := FMagSum[Phase] / 24.0; + FOuterLevel[Phase] := FFixedOuterLevel; StartBurstTracking(Phase); end; Exit; @@ -732,7 +766,9 @@ begin Inc(FSyncCount); FLastSyncTick := NowTick; FLastSyncSample := FSampleNo; - FBestSyncQuality := FMagSum[Phase]; + FBestSyncQuality := SyncEyeOpening(Phase); + FFixedOuterLevel := FMagSum[Phase] / 24.0; + FOuterLevel[Phase] := FFixedOuterLevel; if Kind in [dskMSData, dskMSVoice] then FMobileActiveBurst := True; StartBurstTracking(Phase); finally @@ -795,7 +831,9 @@ begin S.BestSyncDistance := FBestSyncDistance; S.BestSyncPhase := FBestSyncPhase; S.BestCandidateKind := FBestCandidateKind; - if FBestSyncPhase >= 0 then + if FFixedOuterLevel > 0 then + S.SymbolOuterLevel := FFixedOuterLevel + else if FBestSyncPhase >= 0 then S.SymbolOuterLevel := FOuterLevel[FBestSyncPhase]; S.SignalMean := FSignalMean; finally