feat(dmr): decode and route AMBE voice

This commit is contained in:
2026-07-14 18:55:28 +03:00
parent e0e74b5337
commit 28c434d9a7
7 changed files with 231 additions and 10 deletions
+77 -3
View File
@@ -3,9 +3,21 @@ 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.
The CACH/AMBE interleavers and Hamming/Golay matrices are derived from
dsd-fme (src/dmr_bs.c, src/fec.c and include/dmr_const.h).
Copyright (C) 2010 DSD Author
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
}
{$IFDEF FPC}
@@ -25,6 +37,8 @@ type
TDMRBit7 = array[0..6] of Byte;
TDMRBit20 = array[0..19] of Byte;
TDMRBit96 = array[0..95] of Byte;
TDMRAMBEFrame = array[0..95] of Byte;
TDMRAMBEFrames = array[0..2] of TDMRAMBEFrame;
TDMRCACH = record
Valid: Boolean;
@@ -68,6 +82,10 @@ function DMRGolay208Encode(Value: Byte): TDMRBit20;
procedure DMRBPTC19696Encode(const Payload: TDMRBit96;
out Burst: TDMRDibitBurst);
procedure DMRFullLCSetParity(var Payload: TDMRBit96; DataType: Integer);
procedure DMRExtractAMBEFrames(const Burst: TDMRDibitBurst;
out Frames: TDMRAMBEFrames);
procedure DMRInsertAMBEFrames(const Frames: TDMRAMBEFrames;
var Burst: TDMRDibitBurst);
function DMRDecodeCACH(const Burst: TDMRDibitBurst; out CACH: TDMRCACH): Boolean;
function DMRDecodeSlotType(const Burst: TDMRDibitBurst;
out SlotType: TDMRSlotType): Boolean;
@@ -110,6 +128,19 @@ const
HAMMING_15_11_SYNDROME: array[0..14] of Byte =
(9,13,15,14,7,10,5,11,12,6,3, 8,4,2,1);
AMBE_W: array[0..35] of Byte = (
0,1,0,1,0,1, 0,1,0,1,0,1, 0,1,0,1,0,1,
0,1,0,1,0,2, 0,2,0,2,0,2, 0,2,0,2,0,2);
AMBE_X: array[0..35] of Byte = (
23,10,22,9,21,8, 20,7,19,6,18,5, 17,4,16,3,15,2,
14,1,13,0,12,10, 11,9,10,8,9,7, 8,6,7,5,6,4);
AMBE_Y: array[0..35] of Byte = (
0,2,0,2,0,2, 0,2,0,3,0,3, 1,3,1,3,1,3,
1,3,1,3,1,3, 1,3,1,3,1,3, 1,3,1,3,1,3);
AMBE_Z: array[0..35] of Byte = (
5,3,4,2,3,1, 2,0,1,13,0,12, 22,11,21,10,20,9,
19,8,18,7,17,6, 16,5,15,4,14,3, 13,2,12,1,11,0);
function DMRHamming74Encode(Value: Byte): TDMRBit7;
var
i, j: Integer;
@@ -203,6 +234,49 @@ begin
end;
end;
function AMBEDibitIndex(FrameIndex, DibitIndex: Integer): Integer;
begin
case FrameIndex of
0: Result := 12 + DibitIndex;
1: if DibitIndex < 18 then Result := 48 + DibitIndex
else Result := 90 + DibitIndex - 18;
else
Result := 108 + DibitIndex;
end;
end;
procedure DMRExtractAMBEFrames(const Burst: TDMRDibitBurst;
out Frames: TDMRAMBEFrames);
var
FrameIndex, i, DibitIndex: Integer;
Dibit: Byte;
begin
FillChar(Frames, SizeOf(Frames), 0);
for FrameIndex := 0 to 2 do
for i := 0 to 35 do
begin
DibitIndex := AMBEDibitIndex(FrameIndex, i);
Dibit := Burst[DibitIndex];
Frames[FrameIndex][AMBE_W[i] * 24 + AMBE_X[i]] := (Dibit shr 1) and 1;
Frames[FrameIndex][AMBE_Y[i] * 24 + AMBE_Z[i]] := Dibit and 1;
end;
end;
procedure DMRInsertAMBEFrames(const Frames: TDMRAMBEFrames;
var Burst: TDMRDibitBurst);
var
FrameIndex, i, DibitIndex: Integer;
begin
for FrameIndex := 0 to 2 do
for i := 0 to 35 do
begin
DibitIndex := AMBEDibitIndex(FrameIndex, i);
Burst[DibitIndex] :=
((Frames[FrameIndex][AMBE_W[i] * 24 + AMBE_X[i]] and 1) shl 1) or
(Frames[FrameIndex][AMBE_Y[i] * 24 + AMBE_Z[i]] and 1);
end;
end;
function DecodeHamming74(const Received: TDMRBit7; out Value: Byte;
out Corrected: Integer): Boolean;
var