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:
+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.
|
||||
Reference in New Issue
Block a user