From 8315c609d8dbdd4de5cfd85453c79fc6b90b825c Mon Sep 17 00:00:00 2001 From: Alexander UR6LKW Date: Mon, 26 May 2025 19:30:57 +0300 Subject: [PATCH] dmrproto/util to merge voice superframe data --- dmrtools/dmrproto/__init__.py | 1 + dmrtools/dmrproto/util.py | 69 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100755 dmrtools/dmrproto/util.py diff --git a/dmrtools/dmrproto/__init__.py b/dmrtools/dmrproto/__init__.py index 6f67b82..b21bd9e 100755 --- a/dmrtools/dmrproto/__init__.py +++ b/dmrtools/dmrproto/__init__.py @@ -1,3 +1,4 @@ from .mmdvm_l1 import * from .etsi_l2 import * +from .util import * from .exceptions import * diff --git a/dmrtools/dmrproto/util.py b/dmrtools/dmrproto/util.py new file mode 100755 index 0000000..1824b2b --- /dev/null +++ b/dmrtools/dmrproto/util.py @@ -0,0 +1,69 @@ +import logging + +from bitarray import bitarray +from dmr_utils3.bptc import decode_emblc + +from .mmdvm_l1 import DMRPPacketData + + +class EmbLCAssembler: + """ + This is auxiliary class to assemble a few voice packets and + assemble embedded LC from them. + + How to use: pass DMRD packets from the same stream id to process_voicedata, if it returns true, then embedded LC can be decoded with decode() + """ + VTYPE_N_MAP: dict[DMRPPacketData.VoiceType, int] = { + DMRPPacketData.VoiceType.BURST_B: 0, + DMRPPacketData.VoiceType.BURST_C: 1, + DMRPPacketData.VoiceType.BURST_D: 2, + DMRPPacketData.VoiceType.BURST_E: 3 + } + + def __init__(self) -> None: + self.reset() + + def reset(self) -> None: + self.lcs: list[bytes] = list() + self.vseq: int = 0 + + def process_voicedata(self, p: DMRPPacketData) -> bool: + if p.voice_type not in EmbLCAssembler.VTYPE_N_MAP: + return False + + burst_n = EmbLCAssembler.VTYPE_N_MAP[p.voice_type] + + # check voice sequence for next packets + if burst_n > 0 and p.vseq != (self.vseq + 1) % 0x100: + logging.error("Embedded LC failed: wrong vseq " + f"({p.vseq}, expected {self.vseq + 1})") + self.reset() + return False + + # check, if burst sequence matches collected + if len(self.lcs) != burst_n: + logging.error("Embedded LC failed: wrong burst N " + f"({burst_n}, expected {len(self.lcs)})") + self.reset() + return False + + # try getting LC and collect + emblc = p.get_emb_lc() + if emblc is None: + logging.error("Embedded LC failed: can't get") + self.reset() + return False + + self.lcs.append(emblc) + self.vseq = p.vseq + + # logging.debug(f"assembled emblc's: {self.lcs}") + + return burst_n == 3 + + def decode(self) -> bytes|None: + if len(self.lcs) != 4: + return None + + emblc_data: bitarray = bitarray(b"".join(self.lcs), endian='big') + return decode_emblc(emblc_data)