mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat(dmr): decode CACH and slot type
This commit is contained in:
@@ -8,3 +8,9 @@ lazbuild -B --ws=cocoa ewsdr.lpr
|
|||||||
# Optional DMR AMBE+2 adapter (requires mbelib development files)
|
# Optional DMR AMBE+2 adapter (requires mbelib development files)
|
||||||
cmake -S native/dmr -B build/dmr -DCMAKE_BUILD_TYPE=Release
|
cmake -S native/dmr -B build/dmr -DCMAKE_BUILD_TYPE=Release
|
||||||
cmake --build build/dmr
|
cmake --build build/dmr
|
||||||
|
|
||||||
|
# DMR receive/protocol tests
|
||||||
|
fpc -Fu. -FE/tmp tests/dmr_protocol_test.pas
|
||||||
|
/tmp/dmr_protocol_test
|
||||||
|
fpc -Fu. -FE/tmp tests/dmr_frontend_test.pas
|
||||||
|
/tmp/dmr_frontend_test
|
||||||
|
|||||||
+28
-3
@@ -18,7 +18,7 @@ unit DMRDecoder;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, SyncObjs, Math, DMRBindings;
|
Classes, SysUtils, SyncObjs, Math, DMRBindings, DMRProtocol;
|
||||||
|
|
||||||
const
|
const
|
||||||
DMR_INPUT_RATE = 48000;
|
DMR_INPUT_RATE = 48000;
|
||||||
@@ -49,6 +49,13 @@ type
|
|||||||
SignalRMS: Single;
|
SignalRMS: Single;
|
||||||
LastSyncAgeMs: QWord;
|
LastSyncAgeMs: QWord;
|
||||||
VocoderAvailable: Boolean;
|
VocoderAvailable: Boolean;
|
||||||
|
CACHValid: Boolean;
|
||||||
|
Slot: Integer;
|
||||||
|
LCSS: Integer;
|
||||||
|
SlotTypeValid: Boolean;
|
||||||
|
ColorCode: Integer;
|
||||||
|
DataType: Integer;
|
||||||
|
SlotTypeCorrectedBits: Integer;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TDMRDecoder = class;
|
TDMRDecoder = class;
|
||||||
@@ -90,9 +97,10 @@ type
|
|||||||
FMagSum: array[0..DMR_SAMPLES_PER_SYM - 1] of Double;
|
FMagSum: array[0..DMR_SAMPLES_PER_SYM - 1] of Double;
|
||||||
FBestSyncQuality: Double;
|
FBestSyncQuality: Double;
|
||||||
FTrackPhase: Integer;
|
FTrackPhase: Integer;
|
||||||
FCurrentBurst: array[0..DMR_BURST_DIBITS - 1] of Byte;
|
FCurrentBurst: TDMRDibitBurst;
|
||||||
FCurrentBurstPos: Integer;
|
FCurrentBurstPos: Integer;
|
||||||
FLastBurst: array[0..DMR_BURST_DIBITS - 1] of Byte;
|
FLastBurst: TDMRDibitBurst;
|
||||||
|
FLastBurstInfo: TDMRBurstInfo;
|
||||||
FBurstCount: QWord;
|
FBurstCount: QWord;
|
||||||
FRMSSq: Double;
|
FRMSSq: Double;
|
||||||
|
|
||||||
@@ -250,6 +258,7 @@ begin
|
|||||||
FBestSyncQuality := 0;
|
FBestSyncQuality := 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);
|
||||||
FTrackPhase := -1;
|
FTrackPhase := -1;
|
||||||
FCurrentBurstPos := 0;
|
FCurrentBurstPos := 0;
|
||||||
FBurstCount := 0;
|
FBurstCount := 0;
|
||||||
@@ -435,6 +444,7 @@ end;
|
|||||||
procedure TDMRDecoder.CompleteBurst;
|
procedure TDMRDecoder.CompleteBurst;
|
||||||
begin
|
begin
|
||||||
Move(FCurrentBurst[0], FLastBurst[0], SizeOf(FLastBurst));
|
Move(FCurrentBurst[0], FLastBurst[0], SizeOf(FLastBurst));
|
||||||
|
DMRParseBurst(FLastBurst, FLastBurstInfo);
|
||||||
Inc(FBurstCount);
|
Inc(FBurstCount);
|
||||||
FCurrentBurstPos := 0;
|
FCurrentBurstPos := 0;
|
||||||
end;
|
end;
|
||||||
@@ -494,6 +504,21 @@ begin
|
|||||||
(S.LastSyncAgeMs <= DMR_SYNC_HOLD_MS);
|
(S.LastSyncAgeMs <= DMR_SYNC_HOLD_MS);
|
||||||
S.SignalRMS := Sqrt(FRMSSq);
|
S.SignalRMS := Sqrt(FRMSSq);
|
||||||
S.VocoderAvailable := FMbe <> nil;
|
S.VocoderAvailable := FMbe <> nil;
|
||||||
|
// CACH exists on base-station bursts. On MS/direct bursts the same
|
||||||
|
// positions are payload/guard symbols and can accidentally form a
|
||||||
|
// syntactically valid Hamming word.
|
||||||
|
S.CACHValid := FLastBurstInfo.CACH.Valid and
|
||||||
|
(FSyncKind in [dskBSData, dskBSVoice]);
|
||||||
|
S.Slot := FLastBurstInfo.CACH.Slot;
|
||||||
|
S.LCSS := FLastBurstInfo.CACH.LCSS;
|
||||||
|
// Voice sync replaces the centre Slot Type field. Do not expose a
|
||||||
|
// chance Golay match from AMBE bits as a real colour/data type.
|
||||||
|
S.SlotTypeValid := FLastBurstInfo.SlotType.Valid and
|
||||||
|
(FSyncKind in [dskBSData, dskMSData,
|
||||||
|
dskDirectTS1Data, dskDirectTS2Data]);
|
||||||
|
S.ColorCode := FLastBurstInfo.SlotType.ColorCode;
|
||||||
|
S.DataType := FLastBurstInfo.SlotType.DataType;
|
||||||
|
S.SlotTypeCorrectedBits := FLastBurstInfo.SlotType.CorrectedBits;
|
||||||
finally
|
finally
|
||||||
FStatusLock.Leave;
|
FStatusLock.Leave;
|
||||||
end;
|
end;
|
||||||
|
|||||||
+231
@@ -0,0 +1,231 @@
|
|||||||
|
unit DMRProtocol;
|
||||||
|
|
||||||
|
{
|
||||||
|
Small, allocation-free DMR burst protocol layer.
|
||||||
|
|
||||||
|
The CACH interleave and Hamming/Golay generator matrices are derived from
|
||||||
|
dsd-fme (src/dmr_bs.c and src/fec.c). The original code is distributed
|
||||||
|
under the ISC licence; see the dsd-fme source tree for its full notice.
|
||||||
|
}
|
||||||
|
|
||||||
|
{$IFDEF FPC}
|
||||||
|
{$MODE Delphi}
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils;
|
||||||
|
|
||||||
|
const
|
||||||
|
DMR_PROTOCOL_BURST_DIBITS = 144;
|
||||||
|
|
||||||
|
type
|
||||||
|
TDMRDibitBurst = array[0..DMR_PROTOCOL_BURST_DIBITS - 1] of Byte;
|
||||||
|
TDMRBit7 = array[0..6] of Byte;
|
||||||
|
TDMRBit20 = array[0..19] of Byte;
|
||||||
|
|
||||||
|
TDMRCACH = record
|
||||||
|
Valid: Boolean;
|
||||||
|
CorrectedBits: Integer;
|
||||||
|
AccessType: Boolean;
|
||||||
|
Slot: Integer;
|
||||||
|
LCSS: Integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TDMRSlotType = record
|
||||||
|
Valid: Boolean;
|
||||||
|
CorrectedBits: Integer;
|
||||||
|
ColorCode: Integer;
|
||||||
|
DataType: Integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TDMRBurstInfo = record
|
||||||
|
CACH: TDMRCACH;
|
||||||
|
SlotType: TDMRSlotType;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function DMRHamming74Encode(Value: Byte): TDMRBit7;
|
||||||
|
function DMRGolay208Encode(Value: Byte): TDMRBit20;
|
||||||
|
function DMRDecodeCACH(const Burst: TDMRDibitBurst; out CACH: TDMRCACH): Boolean;
|
||||||
|
function DMRDecodeSlotType(const Burst: TDMRDibitBurst;
|
||||||
|
out SlotType: TDMRSlotType): Boolean;
|
||||||
|
procedure DMRParseBurst(const Burst: TDMRDibitBurst; out Info: TDMRBurstInfo);
|
||||||
|
function DMRDataTypeName(DataType: Integer): string;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
const
|
||||||
|
CACH_INTERLEAVE: array[0..23] of Byte = (
|
||||||
|
0, 7, 8, 9, 1, 10,
|
||||||
|
11, 12, 2, 13, 14, 15,
|
||||||
|
3, 16, 4, 17, 18, 19,
|
||||||
|
5, 20, 21, 22, 6, 23
|
||||||
|
);
|
||||||
|
|
||||||
|
HAMMING_7_4_G: array[0..3, 0..6] of Byte = (
|
||||||
|
(1, 0, 0, 0, 1, 0, 1),
|
||||||
|
(0, 1, 0, 0, 1, 1, 1),
|
||||||
|
(0, 0, 1, 0, 1, 1, 0),
|
||||||
|
(0, 0, 0, 1, 0, 1, 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
GOLAY_20_8_G: array[0..7, 0..19] of Byte = (
|
||||||
|
(1,0,0,0,0,0,0,0, 0,0,1,1,1,1,0,1,1,0,1,0),
|
||||||
|
(0,1,0,0,0,0,0,0, 1,1,0,1,1,0,0,1,1,0,0,1),
|
||||||
|
(0,0,1,0,0,0,0,0, 0,1,1,0,1,1,0,0,1,1,0,1),
|
||||||
|
(0,0,0,1,0,0,0,0, 0,0,1,1,0,1,1,0,0,1,1,1),
|
||||||
|
(0,0,0,0,1,0,0,0, 1,1,0,1,1,1,0,0,0,1,1,0),
|
||||||
|
(0,0,0,0,0,1,0,0, 1,0,1,0,1,0,0,1,0,1,1,1),
|
||||||
|
(0,0,0,0,0,0,1,0, 1,0,0,1,0,0,1,1,1,1,1,0),
|
||||||
|
(0,0,0,0,0,0,0,1, 1,0,0,0,1,1,1,0,1,0,1,1)
|
||||||
|
);
|
||||||
|
|
||||||
|
function DMRHamming74Encode(Value: Byte): TDMRBit7;
|
||||||
|
var
|
||||||
|
i, j: Integer;
|
||||||
|
begin
|
||||||
|
FillChar(Result, SizeOf(Result), 0);
|
||||||
|
for i := 0 to 3 do
|
||||||
|
if ((Value shr (3 - i)) and 1) <> 0 then
|
||||||
|
for j := 0 to 6 do Result[j] := Result[j] xor HAMMING_7_4_G[i, j];
|
||||||
|
end;
|
||||||
|
|
||||||
|
function DMRGolay208Encode(Value: Byte): TDMRBit20;
|
||||||
|
var
|
||||||
|
i, j: Integer;
|
||||||
|
begin
|
||||||
|
FillChar(Result, SizeOf(Result), 0);
|
||||||
|
for i := 0 to 7 do
|
||||||
|
if ((Value shr (7 - i)) and 1) <> 0 then
|
||||||
|
for j := 0 to 19 do Result[j] := Result[j] xor GOLAY_20_8_G[i, j];
|
||||||
|
end;
|
||||||
|
|
||||||
|
function DecodeHamming74(const Received: TDMRBit7; out Value: Byte;
|
||||||
|
out Corrected: Integer): Boolean;
|
||||||
|
var
|
||||||
|
Candidate: TDMRBit7;
|
||||||
|
V, i, Distance, BestDistance: Integer;
|
||||||
|
begin
|
||||||
|
Value := 0;
|
||||||
|
BestDistance := 8;
|
||||||
|
for V := 0 to 15 do
|
||||||
|
begin
|
||||||
|
Candidate := DMRHamming74Encode(V);
|
||||||
|
Distance := 0;
|
||||||
|
for i := 0 to 6 do Inc(Distance, Candidate[i] xor (Received[i] and 1));
|
||||||
|
if Distance < BestDistance then
|
||||||
|
begin
|
||||||
|
BestDistance := Distance;
|
||||||
|
Value := V;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Corrected := BestDistance;
|
||||||
|
Result := BestDistance <= 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function DecodeGolay208(const Received: TDMRBit20; out Value: Byte;
|
||||||
|
out Corrected: Integer): Boolean;
|
||||||
|
var
|
||||||
|
Candidate: TDMRBit20;
|
||||||
|
V, i, Distance, BestDistance: Integer;
|
||||||
|
begin
|
||||||
|
Value := 0;
|
||||||
|
BestDistance := 21;
|
||||||
|
for V := 0 to 255 do
|
||||||
|
begin
|
||||||
|
Candidate := DMRGolay208Encode(V);
|
||||||
|
Distance := 0;
|
||||||
|
for i := 0 to 19 do Inc(Distance, Candidate[i] xor (Received[i] and 1));
|
||||||
|
if Distance < BestDistance then
|
||||||
|
begin
|
||||||
|
BestDistance := Distance;
|
||||||
|
Value := V;
|
||||||
|
if Distance = 0 then Break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Corrected := BestDistance;
|
||||||
|
Result := BestDistance <= 2;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function DMRDecodeCACH(const Burst: TDMRDibitBurst; out CACH: TDMRCACH): Boolean;
|
||||||
|
var
|
||||||
|
Deinterleaved: array[0..23] of Byte;
|
||||||
|
TACT: TDMRBit7;
|
||||||
|
Value: Byte;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
FillChar(CACH, SizeOf(CACH), 0);
|
||||||
|
FillChar(Deinterleaved, SizeOf(Deinterleaved), 0);
|
||||||
|
for i := 0 to 11 do
|
||||||
|
begin
|
||||||
|
Deinterleaved[CACH_INTERLEAVE[2 * i]] := (Burst[i] shr 1) and 1;
|
||||||
|
Deinterleaved[CACH_INTERLEAVE[2 * i + 1]] := Burst[i] and 1;
|
||||||
|
end;
|
||||||
|
for i := 0 to 6 do TACT[i] := Deinterleaved[i];
|
||||||
|
CACH.Valid := DecodeHamming74(TACT, Value, CACH.CorrectedBits);
|
||||||
|
if CACH.Valid then
|
||||||
|
begin
|
||||||
|
CACH.AccessType := (Value and 8) <> 0;
|
||||||
|
CACH.Slot := (Value shr 2) and 1;
|
||||||
|
CACH.LCSS := Value and 3;
|
||||||
|
end;
|
||||||
|
Result := CACH.Valid;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function DMRDecodeSlotType(const Burst: TDMRDibitBurst;
|
||||||
|
out SlotType: TDMRSlotType): Boolean;
|
||||||
|
var
|
||||||
|
Bits: TDMRBit20;
|
||||||
|
Value: Byte;
|
||||||
|
i, k: Integer;
|
||||||
|
begin
|
||||||
|
FillChar(SlotType, SizeOf(SlotType), 0);
|
||||||
|
k := 0;
|
||||||
|
for i := 61 to 65 do
|
||||||
|
begin
|
||||||
|
Bits[k] := (Burst[i] shr 1) and 1; Inc(k);
|
||||||
|
Bits[k] := Burst[i] and 1; Inc(k);
|
||||||
|
end;
|
||||||
|
for i := 90 to 94 do
|
||||||
|
begin
|
||||||
|
Bits[k] := (Burst[i] shr 1) and 1; Inc(k);
|
||||||
|
Bits[k] := Burst[i] and 1; Inc(k);
|
||||||
|
end;
|
||||||
|
SlotType.Valid := DecodeGolay208(Bits, Value, SlotType.CorrectedBits);
|
||||||
|
if SlotType.Valid then
|
||||||
|
begin
|
||||||
|
SlotType.ColorCode := (Value shr 4) and $0F;
|
||||||
|
SlotType.DataType := Value and $0F;
|
||||||
|
end;
|
||||||
|
Result := SlotType.Valid;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure DMRParseBurst(const Burst: TDMRDibitBurst; out Info: TDMRBurstInfo);
|
||||||
|
begin
|
||||||
|
FillChar(Info, SizeOf(Info), 0);
|
||||||
|
DMRDecodeCACH(Burst, Info.CACH);
|
||||||
|
DMRDecodeSlotType(Burst, Info.SlotType);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function DMRDataTypeName(DataType: Integer): string;
|
||||||
|
begin
|
||||||
|
case DataType of
|
||||||
|
0: Result := 'PI header';
|
||||||
|
1: Result := 'voice LC';
|
||||||
|
2: Result := 'terminator LC';
|
||||||
|
3: Result := 'CSBK';
|
||||||
|
4: Result := 'MBC header';
|
||||||
|
5: Result := 'MBC continuation';
|
||||||
|
6: Result := 'data header';
|
||||||
|
7: Result := 'rate 1/2 data';
|
||||||
|
8: Result := 'rate 3/4 data';
|
||||||
|
9: Result := 'idle';
|
||||||
|
10: Result := 'rate 1 data';
|
||||||
|
11: Result := 'unified single block';
|
||||||
|
else
|
||||||
|
Result := 'reserved ' + IntToStr(DataType);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
+8
-3
@@ -3587,14 +3587,19 @@ end;
|
|||||||
function TMainForm.DMRStatusText: string;
|
function TMainForm.DMRStatusText: string;
|
||||||
var
|
var
|
||||||
S: TDMRStatus;
|
S: TDMRStatus;
|
||||||
Inv: string;
|
Inv, Details: string;
|
||||||
begin
|
begin
|
||||||
if not FController.GetDMRStatus(S) then Exit('DMR unavailable');
|
if not FController.GetDMRStatus(S) then Exit('DMR unavailable');
|
||||||
if not S.Enabled then Exit('DMR off');
|
if not S.Enabled then Exit('DMR off');
|
||||||
if not S.Synced then Exit(Format('DMR sync... %.3f', [S.SignalRMS]));
|
if not S.Synced then Exit(Format('DMR sync... %.3f', [S.SignalRMS]));
|
||||||
if S.Inverted then Inv := ' INV' else Inv := '';
|
if S.Inverted then Inv := ' INV' else Inv := '';
|
||||||
Result := Format('DMR %s%s #%d',
|
Details := '';
|
||||||
[TDMRDecoder.SyncKindName(S.SyncKind), Inv, S.SyncCount]);
|
if S.CACHValid then Details := Format(' TS%d', [S.Slot + 1]);
|
||||||
|
if S.SlotTypeValid then
|
||||||
|
Details := Details + Format(' CC%d/DT%d', [S.ColorCode, S.DataType]);
|
||||||
|
Result := Format('DMR %s%s%s #%d',
|
||||||
|
[TDMRDecoder.SyncKindName(S.SyncKind), Details, Inv,
|
||||||
|
S.SyncCount]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.UpdateStatusField7;
|
procedure TMainForm.UpdateStatusField7;
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
program dmr_protocol_test;
|
||||||
|
|
||||||
|
{$MODE Delphi}
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils, DMRProtocol;
|
||||||
|
|
||||||
|
const
|
||||||
|
CACH_INTERLEAVE: array[0..23] of Byte = (
|
||||||
|
0, 7, 8, 9, 1, 10,
|
||||||
|
11, 12, 2, 13, 14, 15,
|
||||||
|
3, 16, 4, 17, 18, 19,
|
||||||
|
5, 20, 21, 22, 6, 23
|
||||||
|
);
|
||||||
|
|
||||||
|
procedure PutCACH(var Burst: TDMRDibitBurst; Value: Byte);
|
||||||
|
var
|
||||||
|
Code: TDMRBit7;
|
||||||
|
Deinterleaved, Transmitted: array[0..23] of Byte;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
FillChar(Deinterleaved, SizeOf(Deinterleaved), 0);
|
||||||
|
Code := DMRHamming74Encode(Value);
|
||||||
|
for i := 0 to 6 do Deinterleaved[i] := Code[i];
|
||||||
|
for i := 0 to 23 do Transmitted[i] := Deinterleaved[CACH_INTERLEAVE[i]];
|
||||||
|
for i := 0 to 11 do
|
||||||
|
Burst[i] := (Transmitted[2 * i] shl 1) or Transmitted[2 * i + 1];
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure PutSlotType(var Burst: TDMRDibitBurst; Value: Byte);
|
||||||
|
var
|
||||||
|
Code: TDMRBit20;
|
||||||
|
i, k: Integer;
|
||||||
|
begin
|
||||||
|
Code := DMRGolay208Encode(Value);
|
||||||
|
k := 0;
|
||||||
|
for i := 61 to 65 do
|
||||||
|
begin
|
||||||
|
Burst[i] := (Code[k] shl 1) or Code[k + 1];
|
||||||
|
Inc(k, 2);
|
||||||
|
end;
|
||||||
|
for i := 90 to 94 do
|
||||||
|
begin
|
||||||
|
Burst[i] := (Code[k] shl 1) or Code[k + 1];
|
||||||
|
Inc(k, 2);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure Check(Condition: Boolean; const Message_: string);
|
||||||
|
begin
|
||||||
|
if not Condition then
|
||||||
|
begin
|
||||||
|
WriteLn(Message_);
|
||||||
|
Halt(1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
Burst: TDMRDibitBurst;
|
||||||
|
Info: TDMRBurstInfo;
|
||||||
|
begin
|
||||||
|
FillChar(Burst, SizeOf(Burst), 0);
|
||||||
|
// TACT: AT=1, slot=1 (TS2), LCSS=2. Slot Type: CC=9, data header=6.
|
||||||
|
PutCACH(Burst, $0E);
|
||||||
|
PutSlotType(Burst, $96);
|
||||||
|
DMRParseBurst(Burst, Info);
|
||||||
|
Check(Info.CACH.Valid, 'valid CACH rejected');
|
||||||
|
Check(Info.CACH.AccessType and (Info.CACH.Slot = 1) and
|
||||||
|
(Info.CACH.LCSS = 2), 'TACT fields decoded incorrectly');
|
||||||
|
Check(Info.SlotType.Valid, 'valid Slot Type rejected');
|
||||||
|
Check((Info.SlotType.ColorCode = 9) and (Info.SlotType.DataType = 6),
|
||||||
|
'Slot Type fields decoded incorrectly');
|
||||||
|
|
||||||
|
// Hamming(7,4) corrects one bit and Golay(20,8) corrects two.
|
||||||
|
Burst[0] := Burst[0] xor 2;
|
||||||
|
Burst[61] := Burst[61] xor 2;
|
||||||
|
Burst[92] := Burst[92] xor 1;
|
||||||
|
DMRParseBurst(Burst, Info);
|
||||||
|
Check(Info.CACH.Valid and (Info.CACH.CorrectedBits = 1),
|
||||||
|
'CACH one-bit correction failed');
|
||||||
|
Check(Info.CACH.AccessType and (Info.CACH.Slot = 1) and
|
||||||
|
(Info.CACH.LCSS = 2), 'corrected TACT fields are wrong');
|
||||||
|
Check(Info.SlotType.Valid and (Info.SlotType.CorrectedBits = 2),
|
||||||
|
'Slot Type two-bit correction failed');
|
||||||
|
Check((Info.SlotType.ColorCode = 9) and (Info.SlotType.DataType = 6),
|
||||||
|
'corrected Slot Type fields are wrong');
|
||||||
|
|
||||||
|
// A third Slot Type error exceeds the guaranteed correction radius.
|
||||||
|
Burst[94] := Burst[94] xor 1;
|
||||||
|
DMRParseBurst(Burst, Info);
|
||||||
|
Check(not Info.SlotType.Valid, 'uncorrectable Slot Type accepted');
|
||||||
|
|
||||||
|
WriteLn('DMR CACH and Slot Type tests passed');
|
||||||
|
end.
|
||||||
Reference in New Issue
Block a user