mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
fix(dmr): use flat discriminator and reject noise sync
This commit is contained in:
+77
-3
@@ -140,12 +140,15 @@ type
|
||||
FSyncCount: QWord;
|
||||
FLastSyncTick: QWord;
|
||||
FLastSyncSample: QWord;
|
||||
FPendingSyncSample: array[TDMRSyncKind] of QWord;
|
||||
FPendingSyncCount: array[TDMRSyncKind] of Byte;
|
||||
FMobileActiveBurst: Boolean;
|
||||
|
||||
function PopSamples(var Buf: array of Single): Integer;
|
||||
procedure ProcessSample(S: Single);
|
||||
procedure ProcessSymbol(Phase: Integer; Symbol: Double);
|
||||
procedure NoteSync(Phase: Integer; Kind: TDMRSyncKind; Inverted: Boolean);
|
||||
procedure LoseSync;
|
||||
function Digitize(Phase: Integer; Symbol: Double): Byte;
|
||||
procedure StartBurstTracking(Phase: Integer);
|
||||
procedure CompleteBurst;
|
||||
@@ -174,6 +177,7 @@ const
|
||||
DMR_SYNC_HOLD_MS = 500;
|
||||
DMR_SYNC_MAX_ERRORS = 2;
|
||||
DMR_SUPERFRAME_SAMPLES = DMR_INPUT_RATE * 360 div 1000;
|
||||
DMR_SYNC_CADENCE_TOLERANCE = DMR_SAMPLES_PER_SYM * 3;
|
||||
|
||||
// One bit per DSD-FME sync character: '1'=0, '3'=1. These are the eight
|
||||
// ETSI DMR 48-bit sync patterns after collapsing each dibit to its sign.
|
||||
@@ -337,6 +341,8 @@ begin
|
||||
FSyncCount := 0;
|
||||
FLastSyncTick := 0;
|
||||
FLastSyncSample := 0;
|
||||
FillChar(FPendingSyncSample, SizeOf(FPendingSyncSample), 0);
|
||||
FillChar(FPendingSyncCount, SizeOf(FPendingSyncCount), 0);
|
||||
FMobileActiveBurst := True;
|
||||
finally
|
||||
FStatusLock.Leave;
|
||||
@@ -432,11 +438,19 @@ var
|
||||
Pat: LongWord;
|
||||
Dibit: Byte;
|
||||
V: Double;
|
||||
DeltaSamples, CycleOffset: QWord;
|
||||
DeltaSamples, CycleOffset, PendingSample: QWord;
|
||||
CandidateAllowed: Boolean;
|
||||
begin
|
||||
V := Symbol;
|
||||
if FInputInverted then V := -V;
|
||||
|
||||
// Stop assembling/decoding an endless stream of noise after the carrier
|
||||
// vanishes. Two superframes allow one damaged sync word without losing a
|
||||
// valid call, while still closing the decoder in about 720 ms.
|
||||
if (FLastSyncSample <> 0) and
|
||||
(FSampleNo - FLastSyncSample > 2 * DMR_SUPERFRAME_SAMPLES +
|
||||
DMR_SYNC_CADENCE_TOLERANCE) then
|
||||
LoseSync;
|
||||
Dibit := Digitize(Phase, V);
|
||||
|
||||
// Keep the 90 dibits ending at sync. Once the timing phase is selected this
|
||||
@@ -479,7 +493,6 @@ begin
|
||||
// match lost almost every superframe in recorded handheld-radio IQ.
|
||||
DeltaSamples := FSampleNo - FLastSyncSample;
|
||||
CandidateAllowed := (FLastSyncSample = 0) or
|
||||
(DeltaSamples > DMR_INPUT_RATE * 2) or
|
||||
(SYNC_KIND[i] = FSyncKind);
|
||||
// MS voice/data sync repeats every 360 ms. Cadence gating rejects
|
||||
// chance <=2-bit matches inside AMBE payload while still accepting a
|
||||
@@ -494,13 +507,74 @@ begin
|
||||
end;
|
||||
if (Distance <= DMR_SYNC_MAX_ERRORS) and CandidateAllowed then
|
||||
begin
|
||||
NoteSync(Phase, SYNC_KIND[i], FInputInverted);
|
||||
// Fuzzy 24-symbol matches occur often enough in open-channel noise to
|
||||
// make mbelib emit occasional garbage. Real voice sync repeats every
|
||||
// 360 ms, so require three words of the same kind on that cadence
|
||||
// before opening burst and vocoder processing.
|
||||
if FLastSyncSample = 0 then
|
||||
begin
|
||||
PendingSample := FPendingSyncSample[SYNC_KIND[i]];
|
||||
if PendingSample <> 0 then
|
||||
begin
|
||||
DeltaSamples := FSampleNo - PendingSample;
|
||||
CycleOffset := DeltaSamples mod DMR_SUPERFRAME_SAMPLES;
|
||||
if (DeltaSamples >= DMR_SUPERFRAME_SAMPLES -
|
||||
DMR_SYNC_CADENCE_TOLERANCE) and
|
||||
((CycleOffset <= DMR_SYNC_CADENCE_TOLERANCE) or
|
||||
(CycleOffset >= DMR_SUPERFRAME_SAMPLES -
|
||||
DMR_SYNC_CADENCE_TOLERANCE)) then
|
||||
begin
|
||||
Inc(FPendingSyncCount[SYNC_KIND[i]]);
|
||||
FPendingSyncSample[SYNC_KIND[i]] := FSampleNo;
|
||||
// Three consecutive superframe syncs make accidental capture
|
||||
// in random discriminator noise vanishingly unlikely.
|
||||
if FPendingSyncCount[SYNC_KIND[i]] >= 3 then
|
||||
NoteSync(Phase, SYNC_KIND[i], FInputInverted);
|
||||
end
|
||||
else if DeltaSamples > DMR_SUPERFRAME_SAMPLES +
|
||||
DMR_SYNC_CADENCE_TOLERANCE then
|
||||
begin
|
||||
FPendingSyncSample[SYNC_KIND[i]] := FSampleNo;
|
||||
FPendingSyncCount[SYNC_KIND[i]] := 1;
|
||||
end;
|
||||
end;
|
||||
if FLastSyncSample = 0 then
|
||||
begin
|
||||
if PendingSample = 0 then
|
||||
begin
|
||||
FPendingSyncSample[SYNC_KIND[i]] := FSampleNo;
|
||||
FPendingSyncCount[SYNC_KIND[i]] := 1;
|
||||
end;
|
||||
Exit;
|
||||
end;
|
||||
end
|
||||
else
|
||||
NoteSync(Phase, SYNC_KIND[i], FInputInverted);
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDMRDecoder.LoseSync;
|
||||
begin
|
||||
FStatusLock.Enter;
|
||||
try
|
||||
FSyncKind := dskNone;
|
||||
FInverted := False;
|
||||
FLastSyncTick := 0;
|
||||
FLastSyncSample := 0;
|
||||
FillChar(FPendingSyncSample, SizeOf(FPendingSyncSample), 0);
|
||||
FillChar(FPendingSyncCount, SizeOf(FPendingSyncCount), 0);
|
||||
finally
|
||||
FStatusLock.Leave;
|
||||
end;
|
||||
FTrackPhase := -1;
|
||||
FCurrentBurstPos := 0;
|
||||
FMobileActiveBurst := True;
|
||||
DMRMbeReset(FMbe);
|
||||
end;
|
||||
|
||||
function TDMRDecoder.Digitize(Phase: Integer; Symbol: Double): Byte;
|
||||
var
|
||||
A, Threshold: Double;
|
||||
|
||||
Reference in New Issue
Block a user