diff --git a/DVMHost.vcxproj b/DVMHost.vcxproj
index 8fb674d4..90ee1af0 100644
--- a/DVMHost.vcxproj
+++ b/DVMHost.vcxproj
@@ -155,6 +155,7 @@
+
@@ -230,6 +231,7 @@
+
diff --git a/DVMHost.vcxproj.filters b/DVMHost.vcxproj.filters
index 1e1614a2..4c9d2516 100644
--- a/DVMHost.vcxproj.filters
+++ b/DVMHost.vcxproj.filters
@@ -332,6 +332,9 @@
Header Files\dmr
+
+ Header Files\dmr
+
@@ -532,6 +535,9 @@
Source Files\p25\data
+
+ Source Files\dmr
+
diff --git a/Makefile b/Makefile
index d584a7d0..3f108828 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,7 @@ OBJECTS = \
dmr/lc/LC.o \
dmr/lc/ShortLC.o \
dmr/Control.o \
+ dmr/ControlPacket.o \
dmr/DataPacket.o \
dmr/Slot.o \
dmr/SlotType.o \
diff --git a/Makefile.arm b/Makefile.arm
index 2970f99e..bbe2443a 100644
--- a/Makefile.arm
+++ b/Makefile.arm
@@ -27,6 +27,7 @@ OBJECTS = \
dmr/lc/LC.o \
dmr/lc/ShortLC.o \
dmr/Control.o \
+ dmr/ControlPacket.o \
dmr/DataPacket.o \
dmr/Slot.o \
dmr/SlotType.o \
diff --git a/Makefile.rpi-arm b/Makefile.rpi-arm
index c34be7ed..c7b705ff 100644
--- a/Makefile.rpi-arm
+++ b/Makefile.rpi-arm
@@ -27,6 +27,7 @@ OBJECTS = \
dmr/lc/LC.o \
dmr/lc/ShortLC.o \
dmr/Control.o \
+ dmr/ControlPacket.o \
dmr/DataPacket.o \
dmr/Slot.o \
dmr/SlotType.o \
diff --git a/dmr/ControlPacket.cpp b/dmr/ControlPacket.cpp
new file mode 100644
index 00000000..c91cc879
--- /dev/null
+++ b/dmr/ControlPacket.cpp
@@ -0,0 +1,415 @@
+/**
+* Digital Voice Modem - Host Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Host Software
+*
+*/
+//
+// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
+// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
+//
+/*
+* Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX
+* Copyright (C) 2017-2020 by Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; version 2 of the License.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#include "Defines.h"
+#include "dmr/ControlPacket.h"
+#include "dmr/acl/AccessControl.h"
+#include "dmr/data/DataHeader.h"
+#include "dmr/data/EMB.h"
+#include "dmr/edac/Trellis.h"
+#include "dmr/lc/ShortLC.h"
+#include "dmr/lc/FullLC.h"
+#include "dmr/lc/CSBK.h"
+#include "dmr/SlotType.h"
+#include "dmr/Sync.h"
+#include "edac/BPTC19696.h"
+#include "edac/CRC.h"
+#include "Log.h"
+#include "Utils.h"
+
+using namespace dmr;
+
+#include
+#include
+#include
+#include
+
+// ---------------------------------------------------------------------------
+// Macros
+// ---------------------------------------------------------------------------
+// Don't process RF frames if the network isn't in a idle state.
+#define CHECK_TRAFFIC_COLLISION(_DST_ID) \
+ if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \
+ LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing network traffic!", m_slot->m_slotNo); \
+ return false; \
+ }
+
+#define CHECK_TRAFFIC_COLLISION_DELLC(_DST_ID) \
+ if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \
+ LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing network traffic!", m_slot->m_slotNo); \
+ delete lc; \
+ return false; \
+ }
+
+#define CHECK_TG_HANG(_DST_ID) \
+ if (m_slot->m_rfLastDstId != 0U) { \
+ if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \
+ return; \
+ } \
+ }
+
+#define CHECK_TG_HANG_DELLC(_DST_ID) \
+ if (m_slot->m_rfLastDstId != 0U) { \
+ if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \
+ delete lc; \
+ return; \
+ } \
+ }
+
+// ---------------------------------------------------------------------------
+// Public Class Members
+// ---------------------------------------------------------------------------
+///
+/// Process DMR data frame from the RF interface.
+///
+/// Buffer containing data frame.
+/// Length of data frame.
+///
+bool ControlPacket::process(uint8_t* data, uint32_t len)
+{
+ assert(data != NULL);
+
+ // Get the type from the packet metadata
+ uint8_t dataType = data[1U] & 0x0FU;
+
+ SlotType slotType;
+ slotType.setColorCode(m_slot->m_colorCode);
+ slotType.setDataType(dataType);
+
+ if (dataType == DT_CSBK) {
+ // generate a new CSBK and check validity
+ lc::CSBK csbk = lc::CSBK();
+ csbk.setVerbose(m_dumpCSBKData);
+
+ bool valid = csbk.decode(data + 2U);
+ if (!valid)
+ return false;
+
+ uint8_t csbko = csbk.getCSBKO();
+ if (csbko == CSBKO_BSDWNACT)
+ return false;
+
+ bool gi = csbk.getGI();
+ uint32_t srcId = csbk.getSrcId();
+ uint32_t dstId = csbk.getDstId();
+
+ if (srcId != 0U || dstId != 0U) {
+ CHECK_TRAFFIC_COLLISION(dstId);
+
+ // validate the source RID
+ if (!acl::AccessControl::validateSrcId(srcId)) {
+ LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId);
+ return false;
+ }
+
+ // validate the target ID
+ if (gi) {
+ if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) {
+ LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId);
+ return false;
+ }
+ }
+ }
+
+ // Regenerate the CSBK data
+ csbk.encode(data + 2U);
+
+ // Regenerate the Slot Type
+ slotType.encode(data + 2U);
+
+ // Convert the Data Sync to be from the BS or MS as needed
+ Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
+
+ m_slot->m_rfSeqNo = 0U;
+
+ data[0U] = TAG_DATA;
+ data[1U] = 0x00U;
+
+ if (m_slot->m_duplex)
+ m_slot->writeQueueRF(data);
+
+ m_slot->writeNetworkRF(data, DT_CSBK, gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId);
+
+ if (m_verbose) {
+ switch (csbko) {
+ case CSBKO_UU_V_REQ:
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_UU_V_REQ (Unit to Unit Voice Service Request), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+ break;
+ case CSBKO_UU_ANS_RSP:
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_UU_ANS_RSP (Unit to Unit Voice Service Answer Response), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+ break;
+ case CSBKO_NACK_RSP:
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_NACK_RSP (Negative Acknowledgment Response), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+ break;
+ case CSBKO_CALL_ALRT:
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_CALL_ALRT (Call Alert), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+
+ ::ActivityLog("DMR", true, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId);
+ break;
+ case CSBKO_ACK_RSP:
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_ACK_RSP (Acknowledge Response), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+
+ ::ActivityLog("DMR", true, "Slot %u ack response from %u to %u", m_slot->m_slotNo, srcId, dstId);
+ break;
+ case CSBKO_EXT_FNCT:
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_EXT_FNCT (Extended Function), op = $%02X, arg = %u, tgt = %u",
+ m_slot->m_slotNo, csbk.getCBF(), dstId, srcId);
+ }
+
+ // generate activity log entry
+ if (csbk.getCBF() == DMR_EXT_FNCT_CHECK) {
+ ::ActivityLog("DMR", true, "Slot %u radio check request from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT) {
+ ::ActivityLog("DMR", true, "Slot %u radio inhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT) {
+ ::ActivityLog("DMR", true, "Slot %u radio uninhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_CHECK_ACK) {
+ ::ActivityLog("DMR", true, "Slot %u radio check response from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT_ACK) {
+ ::ActivityLog("DMR", true, "Slot %u radio inhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT_ACK) {
+ ::ActivityLog("DMR", true, "Slot %u radio uninhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ break;
+ case CSBKO_PRECCSBK:
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_PRECCSBK (%s Preamble CSBK), toFollow = %u, src = %u, dst = %s%u",
+ m_slot->m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), srcId, gi ? "TG " : "", dstId);
+ }
+ break;
+ default:
+ LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, unhandled CSBK, csbko = $%02X, fid = $%02X", m_slot->m_slotNo, csbko, csbk.getFID());
+ break;
+ }
+ }
+
+ if (m_debug) {
+ Utils::dump(2U, "!!! *TX DMR Frame - DT_CSBK", data + 2U, DMR_FRAME_LENGTH_BYTES);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+///
+/// Process a data frame from the network.
+///
+///
+void ControlPacket::processNetwork(const data::Data & dmrData)
+{
+ uint8_t dataType = dmrData.getDataType();
+
+ uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
+ dmrData.getData(data + 2U);
+
+ if (dataType == DT_CSBK) {
+ lc::CSBK csbk = lc::CSBK();
+ csbk.setVerbose(m_dumpCSBKData);
+
+ bool valid = csbk.decode(data + 2U);
+ if (!valid) {
+ LogError(LOG_NET, "DMR Slot %u, DT_CSBK, unable to decode the network CSBK", m_slot->m_slotNo);
+ return;
+ }
+
+ uint8_t csbko = csbk.getCSBKO();
+ if (csbko == CSBKO_BSDWNACT)
+ return;
+
+ bool gi = csbk.getGI();
+ uint32_t srcId = csbk.getSrcId();
+ uint32_t dstId = csbk.getDstId();
+
+ CHECK_TG_HANG(dstId);
+
+ // Regenerate the CSBK data
+ csbk.encode(data + 2U);
+
+ // Regenerate the Slot Type
+ SlotType slotType;
+ slotType.decode(data + 2U);
+ slotType.setColorCode(m_slot->m_colorCode);
+ slotType.encode(data + 2U);
+
+ // Convert the Data Sync to be from the BS or MS as needed
+ Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
+
+ data[0U] = TAG_DATA;
+ data[1U] = 0x00U;
+
+ if (csbko == CSBKO_PRECCSBK && csbk.getDataContent()) {
+ uint32_t cbf = NO_PREAMBLE_CSBK + csbk.getCBF() - 1U;
+ for (uint32_t i = 0U; i < NO_PREAMBLE_CSBK; i++, cbf--) {
+ // Change blocks to follow
+ csbk.setCBF(cbf);
+
+ // Regenerate the CSBK data
+ csbk.encode(data + 2U);
+
+ // Regenerate the Slot Type
+ SlotType slotType;
+ slotType.decode(data + 2U);
+ slotType.setColorCode(m_slot->m_colorCode);
+ slotType.encode(data + 2U);
+
+ // Convert the Data Sync to be from the BS or MS as needed
+ Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
+
+ m_slot->writeQueueNet(data);
+ }
+ }
+ else
+ m_slot->writeQueueNet(data);
+
+ if (m_verbose) {
+ switch (csbko) {
+ case CSBKO_UU_V_REQ:
+ if (m_verbose) {
+ LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_UU_V_REQ (Unit to Unit Voice Service Request), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+ break;
+ case CSBKO_UU_ANS_RSP:
+ if (m_verbose) {
+ LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_UU_ANS_RSP (Unit to Unit Voice Service Answer Response), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+ break;
+ case CSBKO_NACK_RSP:
+ if (m_verbose) {
+ LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_NACK_RSP (Negative Acknowledgment Response), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+ break;
+ case CSBKO_CALL_ALRT:
+ if (m_verbose) {
+ LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_CALL_ALRT (Call Alert), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+
+ ::ActivityLog("DMR", false, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId);
+ break;
+ case CSBKO_ACK_RSP:
+ if (m_verbose) {
+ LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_ACK_RSP (Acknowledge Response), src = %u, dst = %s%u",
+ m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
+ }
+
+ ::ActivityLog("DMR", false, "Slot %u ack response from %u to %u", m_slot->m_slotNo, srcId, dstId);
+ break;
+ case CSBKO_EXT_FNCT:
+ if (m_verbose) {
+ LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_EXT_FNCT (Extended Function), op = $%02X, arg = %u, tgt = %u",
+ m_slot->m_slotNo, csbk.getCBF(), dstId, srcId);
+ }
+
+ // generate activity log entry
+ if (csbk.getCBF() == DMR_EXT_FNCT_CHECK) {
+ ::ActivityLog("DMR", false, "Slot %u radio check request from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT) {
+ ::ActivityLog("DMR", false, "Slot %u radio inhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT) {
+ ::ActivityLog("DMR", false, "Slot %u radio uninhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_CHECK_ACK) {
+ ::ActivityLog("DMR", false, "Slot %u radio check response from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT_ACK) {
+ ::ActivityLog("DMR", false, "Slot %u radio inhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT_ACK) {
+ ::ActivityLog("DMR", false, "Slot %u radio uninhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId);
+ }
+ break;
+ case CSBKO_PRECCSBK:
+ if (m_verbose) {
+ LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_PRECCSBK (%s Preamble CSBK), toFollow = %u, src = %u, dst = %s%u",
+ m_slot->m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), srcId, gi ? "TG " : "", dstId);
+ }
+ break;
+ default:
+ LogWarning(LOG_NET, "DMR Slot %u, DT_CSBK, unhandled network CSBK, csbko = $%02X, fid = $%02X", m_slot->m_slotNo, csbko, csbk.getFID());
+ break;
+ }
+ }
+ }
+ else {
+ // Unhandled data type
+ LogWarning(LOG_NET, "DMR Slot %u, unhandled network data, type = $%02X", m_slot->m_slotNo, dataType);
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Private Class Members
+// ---------------------------------------------------------------------------
+///
+/// Initializes a new instance of the ControlPacket class.
+///
+/// DMR slot.
+/// Instance of the BaseNetwork class.
+///
+/// Flag indicating whether DMR debug is enabled.
+/// Flag indicating whether DMR verbose logging is enabled.
+ControlPacket::ControlPacket(Slot * slot, network::BaseNetwork * network, bool dumpCSBKData, bool debug, bool verbose) :
+ m_slot(slot),
+ m_dumpCSBKData(dumpCSBKData),
+ m_verbose(verbose),
+ m_debug(debug)
+{
+ /* stub */
+}
+
+///
+/// Finalizes a instance of the ControlPacket class.
+///
+ControlPacket::~ControlPacket()
+{
+ /* stub */
+}
diff --git a/dmr/ControlPacket.h b/dmr/ControlPacket.h
new file mode 100644
index 00000000..1ce59e20
--- /dev/null
+++ b/dmr/ControlPacket.h
@@ -0,0 +1,85 @@
+/**
+* Digital Voice Modem - Host Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Host Software
+*
+*/
+//
+// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
+// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
+//
+/*
+* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
+* Copyright (C) 2017-2020 by Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#if !defined(__DMR_CONTROL_PACKET_H__)
+#define __DMR_CONTROL_PACKET_H__
+
+#include "Defines.h"
+#include "dmr/data/Data.h"
+#include "dmr/data/EmbeddedData.h"
+#include "dmr/lc/LC.h"
+#include "dmr/Slot.h"
+#include "modem/Modem.h"
+#include "network/BaseNetwork.h"
+#include "RingBuffer.h"
+#include "lookups/RadioIdLookup.h"
+#include "lookups/TalkgroupIdLookup.h"
+#include "StopWatch.h"
+#include "Timer.h"
+
+#include
+
+namespace dmr
+{
+ // ---------------------------------------------------------------------------
+ // Class Prototypes
+ // ---------------------------------------------------------------------------
+ class HOST_SW_API DataPacket;
+ class HOST_SW_API Slot;
+
+ // ---------------------------------------------------------------------------
+ // Class Declaration
+ // This class implements core logic for handling DMR control (CSBK) packets.
+ // ---------------------------------------------------------------------------
+
+ class HOST_SW_API ControlPacket {
+ public:
+ /// Process a data frame from the RF interface.
+ bool process(uint8_t* data, uint32_t len);
+ /// Process a data frame from the network.
+ void processNetwork(const data::Data& dmrData);
+
+ private:
+ friend class DataPacket;
+ friend class Slot;
+ Slot* m_slot;
+
+ bool m_dumpCSBKData;
+ bool m_verbose;
+ bool m_debug;
+
+ /// Initializes a new instance of the ControlPacket class.
+ ControlPacket(Slot* slot, network::BaseNetwork* network, bool dumpCSBKData, bool debug, bool verbose);
+ /// Finalizes a instance of the DataPacket class.
+ ~ControlPacket();
+ };
+} // namespace dmr
+
+#endif // __DMR_CONTROL_PACKET_H__
diff --git a/dmr/DataPacket.cpp b/dmr/DataPacket.cpp
index c42a6fb0..07585ee0 100644
--- a/dmr/DataPacket.cpp
+++ b/dmr/DataPacket.cpp
@@ -98,160 +98,13 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
slotType.setColorCode(m_slot->m_colorCode);
slotType.setDataType(dataType);
- if (dataType == DT_VOICE_LC_HEADER) {
- if (m_slot->m_rfState == RS_RF_AUDIO)
- return true;
-
- lc::FullLC fullLC;
- lc::LC* lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER);
- if (lc == NULL)
- return false;
-
- uint32_t srcId = lc->getSrcId();
- uint32_t dstId = lc->getDstId();
- uint8_t flco = lc->getFLCO();
-
- CHECK_TRAFFIC_COLLISION_DELLC(dstId);
-
- // validate source RID
- if (!acl::AccessControl::validateSrcId(srcId)) {
- if (m_lastRejectId == 0U || m_lastRejectId == srcId) {
- LogWarning(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId);
- ::ActivityLog("DMR", true, "Slot %u RF voice rejection from %u to %s%u ", m_slot->m_slotNo, srcId, flco == FLCO_GROUP ? "TG " : "", dstId);
- }
-
- m_slot->m_rfLastDstId = 0U;
- m_slot->m_rfTGHang.stop();
-
- delete lc;
- return false;
- }
-
- // validate target TID, if the target is a talkgroup
- if (flco == FLCO_GROUP) {
- if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) {
- if (m_lastRejectId == 0U || m_lastRejectId == dstId) {
- LogWarning(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId);
- ::ActivityLog("DMR", true, "Slot %u RF voice rejection from %u to TG %u ", m_slot->m_slotNo, srcId, dstId);
- }
-
- m_slot->m_rfLastDstId = 0U;
- m_slot->m_rfTGHang.stop();
-
- delete lc;
- return false;
- }
- }
-
- m_lastRejectId = 0U;
-
- if (m_verbose) {
- LogMessage(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF());
- }
-
- uint8_t fid = lc->getFID();
-
- // NOTE: this is fiddly -- on Motorola a FID of 0x10 indicates a SU has transmitted with Enhanced Privacy enabled -- this might change
- // and is not exact science!
- bool encrypted = (fid & 0x10U) == 0x10U;
-
- m_rfLC = lc;
-
- // The standby LC data
- m_slot->m_voice->m_rfEmbeddedLC.setLC(*m_rfLC);
- m_slot->m_voice->m_rfEmbeddedData[0U].setLC(*m_rfLC);
- m_slot->m_voice->m_rfEmbeddedData[1U].setLC(*m_rfLC);
-
- // Regenerate the LC data
- fullLC.encode(*m_rfLC, data + 2U, DT_VOICE_LC_HEADER);
-
- // Regenerate the Slot Type
- slotType.encode(data + 2U);
-
- // Convert the Data Sync to be from the BS or MS as needed
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- data[0U] = TAG_DATA;
- data[1U] = 0x00U;
-
- m_slot->m_rfTimeoutTimer.start();
- m_slot->m_rfTimeout = false;
-
- m_slot->m_rfFrames = 0U;
- m_slot->m_rfSeqNo = 0U;
- m_slot->m_rfBits = 1U;
- m_slot->m_rfErrs = 0U;
-
- m_slot->m_voice->m_rfEmbeddedReadN = 0U;
- m_slot->m_voice->m_rfEmbeddedWriteN = 1U;
- m_slot->m_voice->m_rfTalkerId = TALKER_ID_NONE;
-
- m_slot->m_minRSSI = m_slot->m_rssi;
- m_slot->m_maxRSSI = m_slot->m_rssi;
- m_slot->m_aveRSSI = m_slot->m_rssi;
- m_slot->m_rssiCount = 1U;
-
- if (m_slot->m_duplex) {
- m_slot->m_queue.clear();
- m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo);
-
- for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
- m_slot->writeQueueRF(data);
- }
-
- m_slot->writeNetworkRF(data, DT_VOICE_LC_HEADER);
-
- m_slot->m_rfState = RS_RF_AUDIO;
- m_slot->m_rfLastDstId = dstId;
-
- if (m_slot->m_netState == RS_NET_IDLE) {
- m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true);
- }
-
- if (m_debug) {
- Utils::dump(2U, "!!! *TX DMR Frame - DT_VOICE_LC_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES);
- }
-
- ::ActivityLog("DMR", true, "Slot %u RF %svoice header from %u to %s%u", m_slot->m_slotNo, encrypted ? "encrypted " : "", srcId, flco == FLCO_GROUP ? "TG " : "", dstId);
- return true;
- }
- else if (dataType == DT_VOICE_PI_HEADER) {
- if (m_slot->m_rfState != RS_RF_AUDIO)
- return false;
-
- // Regenerate the Slot Type
- slotType.encode(data + 2U);
-
- // Convert the Data Sync to be from the BS or MS as needed
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- // Regenerate the payload and the BPTC (196,96) FEC
- edac::BPTC19696 bptc;
- uint8_t payload[12U];
- bptc.decode(data + 2U, payload);
- bptc.encode(payload, data + 2U);
-
- data[0U] = TAG_DATA;
- data[1U] = 0x00U;
-
- if (m_slot->m_duplex)
- m_slot->writeQueueRF(data);
-
- m_slot->writeNetworkRF(data, DT_VOICE_PI_HEADER);
-
- if (m_debug) {
- Utils::dump(2U, "!!! *TX DMR Frame - DT_VOICE_PI_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES);
- }
-
- return true;
- }
- else if (dataType == DT_TERMINATOR_WITH_LC) {
+ if (dataType == DT_TERMINATOR_WITH_LC) {
if (m_slot->m_rfState != RS_RF_AUDIO)
return false;
// Regenerate the LC data
lc::FullLC fullLC;
- fullLC.encode(*m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC);
+ fullLC.encode(*m_slot->m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC);
// Regenerate the Slot Type
slotType.encode(data + 2U);
@@ -289,148 +142,14 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
m_slot->m_slotNo, m_slot->m_rfFrames, m_slot->m_rfBits, m_slot->m_rfErrs, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits));
if (m_slot->m_rfTimeout) {
- writeEndRF();
+ m_slot->writeEndRF();
return false;
}
else {
- writeEndRF();
+ m_slot->writeEndRF();
return true;
}
}
- else if (dataType == DT_CSBK) {
- // generate a new CSBK and check validity
- lc::CSBK csbk = lc::CSBK();
- csbk.setVerbose(m_dumpCSBKData);
-
- bool valid = csbk.decode(data + 2U);
- if (!valid)
- return false;
-
- uint8_t csbko = csbk.getCSBKO();
- if (csbko == CSBKO_BSDWNACT)
- return false;
-
- bool gi = csbk.getGI();
- uint32_t srcId = csbk.getSrcId();
- uint32_t dstId = csbk.getDstId();
-
- if (srcId != 0U || dstId != 0U) {
- CHECK_TRAFFIC_COLLISION(dstId);
-
- // validate the source RID
- if (!acl::AccessControl::validateSrcId(srcId)) {
- LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId);
- return false;
- }
-
- // validate the target ID
- if (gi) {
- if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) {
- LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId);
- return false;
- }
- }
- }
-
- // Regenerate the CSBK data
- csbk.encode(data + 2U);
-
- // Regenerate the Slot Type
- slotType.encode(data + 2U);
-
- // Convert the Data Sync to be from the BS or MS as needed
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- m_slot->m_rfSeqNo = 0U;
-
- data[0U] = TAG_DATA;
- data[1U] = 0x00U;
-
- if (m_slot->m_duplex)
- m_slot->writeQueueRF(data);
-
- m_slot->writeNetworkRF(data, DT_CSBK, gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId);
-
- if (m_verbose) {
- switch (csbko) {
- case CSBKO_UU_V_REQ:
- if (m_verbose) {
- LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_UU_V_REQ (Unit to Unit Voice Service Request), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
- break;
- case CSBKO_UU_ANS_RSP:
- if (m_verbose) {
- LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_UU_ANS_RSP (Unit to Unit Voice Service Answer Response), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
- break;
- case CSBKO_NACK_RSP:
- if (m_verbose) {
- LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_NACK_RSP (Negative Acknowledgment Response), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
- break;
- case CSBKO_CALL_ALRT:
- if (m_verbose) {
- LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_CALL_ALRT (Call Alert), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
-
- ::ActivityLog("DMR", true, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId);
- break;
- case CSBKO_ACK_RSP:
- if (m_verbose) {
- LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_ACK_RSP (Acknowledge Response), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
-
- ::ActivityLog("DMR", true, "Slot %u ack response from %u to %u", m_slot->m_slotNo, srcId, dstId);
- break;
- case CSBKO_EXT_FNCT:
- if (m_verbose) {
- LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_EXT_FNCT (Extended Function), op = $%02X, arg = %u, tgt = %u",
- m_slot->m_slotNo, csbk.getCBF(), dstId, srcId);
- }
-
- // generate activity log entry
- if (csbk.getCBF() == DMR_EXT_FNCT_CHECK) {
- ::ActivityLog("DMR", true, "Slot %u radio check request from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT) {
- ::ActivityLog("DMR", true, "Slot %u radio inhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT) {
- ::ActivityLog("DMR", true, "Slot %u radio uninhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_CHECK_ACK) {
- ::ActivityLog("DMR", true, "Slot %u radio check response from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT_ACK) {
- ::ActivityLog("DMR", true, "Slot %u radio inhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT_ACK) {
- ::ActivityLog("DMR", true, "Slot %u radio uninhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- break;
- case CSBKO_PRECCSBK:
- if (m_verbose) {
- LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_PRECCSBK (%s Preamble CSBK), toFollow = %u, src = %u, dst = %s%u",
- m_slot->m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), srcId, gi ? "TG " : "", dstId);
- }
- break;
- default:
- LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, unhandled CSBK, csbko = $%02X, fid = $%02X", m_slot->m_slotNo, csbko, csbk.getFID());
- break;
- }
- }
-
- if (m_debug) {
- Utils::dump(2U, "!!! *TX DMR Frame - DT_CSBK", data + 2U, DMR_FRAME_LENGTH_BYTES);
- }
-
- return true;
- }
else if (dataType == DT_DATA_HEADER) {
if (m_slot->m_rfState == RS_RF_DATA)
return true;
@@ -462,7 +181,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
m_slot->m_rfFrames = dataHeader.getBlocks();
m_slot->m_rfSeqNo = 0U;
- m_rfLC = new lc::LC(gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId);
+ m_slot->m_rfLC = new lc::LC(gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId);
// Regenerate the data header
dataHeader.encode(data + 2U);
@@ -499,7 +218,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
if (m_slot->m_rfFrames == 0U) {
::ActivityLog("DMR", true, "Slot %u ended RF data transmission", m_slot->m_slotNo);
- writeEndRF();
+ m_slot->writeEndRF();
}
return true;
@@ -566,7 +285,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
}
LogMessage(LOG_RF, "DMR Slot %u, DT_RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo);
- writeEndRF();
+ m_slot->writeEndRF();
}
if (m_debug) {
@@ -590,190 +309,13 @@ void DataPacket::processNetwork(const data::Data& dmrData)
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
dmrData.getData(data + 2U);
- if (dataType == DT_VOICE_LC_HEADER) {
- if (m_slot->m_netState == RS_NET_AUDIO)
- return;
-
- lc::FullLC fullLC;
- lc::LC * lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER);
- if (lc == NULL) {
- LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, bad LC received from the network, replacing", m_slot->m_slotNo);
- lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId());
- }
-
- uint32_t srcId = lc->getSrcId();
- uint32_t dstId = lc->getDstId();
- uint8_t flco = lc->getFLCO();
-
- CHECK_TG_HANG_DELLC(dstId);
-
- if (dstId != dmrData.getDstId() || srcId != dmrData.getSrcId() || flco != dmrData.getFLCO())
- LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, header doesn't match the DMR RF header: %u->%s%u %u->%s%u", m_slot->m_slotNo,
- dmrData.getSrcId(), dmrData.getFLCO() == FLCO_GROUP ? "TG" : "", dmrData.getDstId(),
- srcId, flco == FLCO_GROUP ? "TG" : "", dstId);
-
- if (m_verbose) {
- LogMessage(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF());
- }
-
- m_netLC = lc;
-
- // The standby LC data
- m_slot->m_voice->m_netEmbeddedLC.setLC(*m_netLC);
- m_slot->m_voice->m_netEmbeddedData[0U].setLC(*m_netLC);
- m_slot->m_voice->m_netEmbeddedData[1U].setLC(*m_netLC);
-
- // Regenerate the LC data
- fullLC.encode(*m_netLC, data + 2U, DT_VOICE_LC_HEADER);
-
- // Regenerate the Slot Type
- SlotType slotType;
- slotType.setColorCode(m_slot->m_colorCode);
- slotType.setDataType(DT_VOICE_LC_HEADER);
- slotType.encode(data + 2U);
-
- // Convert the Data Sync to be from the BS or MS as needed
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- data[0U] = TAG_DATA;
- data[1U] = 0x00U;
-
- m_slot->m_voice->m_lastFrameValid = false;
-
- m_slot->m_netTimeoutTimer.start();
- m_slot->m_netTimeout = false;
-
- m_slot->m_netFrames = 0U;
- m_slot->m_netLost = 0U;
- m_slot->m_netBits = 1U;
- m_slot->m_netErrs = 0U;
-
- m_slot->m_voice->m_netEmbeddedReadN = 0U;
- m_slot->m_voice->m_netEmbeddedWriteN = 1U;
- m_slot->m_voice->m_netTalkerId = TALKER_ID_NONE;
-
- if (m_slot->m_duplex) {
- m_slot->m_queue.clear();
- m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo);
- }
-
- for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++)
- m_slot->writeQueueNet(m_slot->m_idle);
-
- if (m_slot->m_duplex) {
- for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
- m_slot->writeQueueNet(data);
- }
- else {
- for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++)
- m_slot->writeQueueNet(data);
- }
-
- m_slot->m_netState = RS_NET_AUDIO;
- m_slot->m_netLastDstId = dstId;
-
- m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true);
-
- if (m_debug) {
- Utils::dump(2U, "!!! *TX DMR Network Frame - DT_VOICE_LC_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES);
- }
-
- ::ActivityLog("DMR", false, "Slot %u network voice header from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO_GROUP ? "TG " : "", dstId);
- }
- else if (dataType == DT_VOICE_PI_HEADER) {
- if (m_slot->m_netState != RS_NET_AUDIO) {
- lc::LC* lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId());
-
- uint32_t srcId = lc->getSrcId();
- uint32_t dstId = lc->getDstId();
-
- CHECK_TG_HANG_DELLC(dstId);
-
- m_netLC = lc;
-
- m_slot->m_voice->m_lastFrameValid = false;
-
- m_slot->m_netTimeoutTimer.start();
- m_slot->m_netTimeout = false;
-
- if (m_slot->m_duplex) {
- m_slot->m_queue.clear();
- m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo);
- }
-
- for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++)
- m_slot->writeQueueNet(m_slot->m_idle);
-
- // Create a dummy start frame
- uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U];
-
- Sync::addDMRDataSync(start + 2U, m_slot->m_duplex);
-
- lc::FullLC fullLC;
- fullLC.encode(*m_netLC, start + 2U, DT_VOICE_LC_HEADER);
-
- SlotType slotType;
- slotType.setColorCode(m_slot->m_colorCode);
- slotType.setDataType(DT_VOICE_LC_HEADER);
- slotType.encode(start + 2U);
-
- start[0U] = TAG_DATA;
- start[1U] = 0x00U;
-
- if (m_slot->m_duplex) {
- for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
- m_slot->writeQueueRF(start);
- }
- else {
- for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++)
- m_slot->writeQueueRF(start);
- }
-
- m_slot->m_netFrames = 0U;
- m_slot->m_netLost = 0U;
- m_slot->m_netBits = 1U;
- m_slot->m_netErrs = 0U;
-
- m_slot->m_netState = RS_NET_AUDIO;
- m_slot->m_netLastDstId = dstId;
-
- m_slot->setShortLC(m_slot->m_slotNo, dstId, m_netLC->getFLCO(), true);
-
- ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u",
- m_slot->m_slotNo, srcId, m_netLC->getFLCO() == FLCO_GROUP ? "TG " : "", dstId);
- }
-
- // Regenerate the Slot Type
- SlotType slotType;
- slotType.setColorCode(m_slot->m_colorCode);
- slotType.setDataType(DT_VOICE_PI_HEADER);
- slotType.encode(data + 2U);
-
- // Convert the Data Sync to be from the BS or MS as needed
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- // Regenerate the payload and the BPTC (196,96) FEC
- edac::BPTC19696 bptc;
- uint8_t payload[12U];
- bptc.decode(data + 2U, payload);
- bptc.encode(payload, data + 2U);
-
- data[0U] = TAG_DATA;
- data[1U] = 0x00U;
-
- m_slot->writeQueueNet(data);
-
- if (m_debug) {
- Utils::dump(2U, "!!! *TX DMR Network Frame - DT_VOICE_PI_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES);
- }
- }
- else if (dataType == DT_TERMINATOR_WITH_LC) {
+ if (dataType == DT_TERMINATOR_WITH_LC) {
if (m_slot->m_netState != RS_NET_AUDIO)
return;
// Regenerate the LC data
lc::FullLC fullLC;
- fullLC.encode(*m_netLC, data + 2U, DT_TERMINATOR_WITH_LC);
+ fullLC.encode(*m_slot->m_netLC, data + 2U, DT_TERMINATOR_WITH_LC);
// Regenerate the Slot Type
SlotType slotType;
@@ -807,140 +349,7 @@ void DataPacket::processNetwork(const data::Data& dmrData)
::ActivityLog("DMR", false, "Slot %u network end of voice transmission, %.1f seconds, %u%% packet loss, BER: %.1f%%",
m_slot->m_slotNo, float(m_slot->m_netFrames) / 16.667F, (m_slot->m_netLost * 100U) / m_slot->m_netFrames, float(m_slot->m_netErrs * 100U) / float(m_slot->m_netBits));
- writeEndNet();
- }
- else if (dataType == DT_CSBK) {
- lc::CSBK csbk = lc::CSBK();
- csbk.setVerbose(m_dumpCSBKData);
-
- bool valid = csbk.decode(data + 2U);
- if (!valid) {
- LogError(LOG_NET, "DMR Slot %u, DT_CSBK, unable to decode the network CSBK", m_slot->m_slotNo);
- return;
- }
-
- uint8_t csbko = csbk.getCSBKO();
- if (csbko == CSBKO_BSDWNACT)
- return;
-
- bool gi = csbk.getGI();
- uint32_t srcId = csbk.getSrcId();
- uint32_t dstId = csbk.getDstId();
-
- CHECK_TG_HANG(dstId);
-
- // Regenerate the CSBK data
- csbk.encode(data + 2U);
-
- // Regenerate the Slot Type
- SlotType slotType;
- slotType.decode(data + 2U);
- slotType.setColorCode(m_slot->m_colorCode);
- slotType.encode(data + 2U);
-
- // Convert the Data Sync to be from the BS or MS as needed
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- data[0U] = TAG_DATA;
- data[1U] = 0x00U;
-
- if (csbko == CSBKO_PRECCSBK && csbk.getDataContent()) {
- uint32_t cbf = NO_PREAMBLE_CSBK + csbk.getCBF() - 1U;
- for (uint32_t i = 0U; i < NO_PREAMBLE_CSBK; i++, cbf--) {
- // Change blocks to follow
- csbk.setCBF(cbf);
-
- // Regenerate the CSBK data
- csbk.encode(data + 2U);
-
- // Regenerate the Slot Type
- SlotType slotType;
- slotType.decode(data + 2U);
- slotType.setColorCode(m_slot->m_colorCode);
- slotType.encode(data + 2U);
-
- // Convert the Data Sync to be from the BS or MS as needed
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- m_slot->writeQueueNet(data);
- }
- }
- else
- m_slot->writeQueueNet(data);
-
- if (m_verbose) {
- switch (csbko) {
- case CSBKO_UU_V_REQ:
- if (m_verbose) {
- LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_UU_V_REQ (Unit to Unit Voice Service Request), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
- break;
- case CSBKO_UU_ANS_RSP:
- if (m_verbose) {
- LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_UU_ANS_RSP (Unit to Unit Voice Service Answer Response), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
- break;
- case CSBKO_NACK_RSP:
- if (m_verbose) {
- LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_NACK_RSP (Negative Acknowledgment Response), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
- break;
- case CSBKO_CALL_ALRT:
- if (m_verbose) {
- LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_CALL_ALRT (Call Alert), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
-
- ::ActivityLog("DMR", false, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId);
- break;
- case CSBKO_ACK_RSP:
- if (m_verbose) {
- LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_ACK_RSP (Acknowledge Response), src = %u, dst = %s%u",
- m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId);
- }
-
- ::ActivityLog("DMR", false, "Slot %u ack response from %u to %u", m_slot->m_slotNo, srcId, dstId);
- break;
- case CSBKO_EXT_FNCT:
- if (m_verbose) {
- LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_EXT_FNCT (Extended Function), op = $%02X, arg = %u, tgt = %u",
- m_slot->m_slotNo, csbk.getCBF(), dstId, srcId);
- }
-
- // generate activity log entry
- if (csbk.getCBF() == DMR_EXT_FNCT_CHECK) {
- ::ActivityLog("DMR", false, "Slot %u radio check request from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT) {
- ::ActivityLog("DMR", false, "Slot %u radio inhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT) {
- ::ActivityLog("DMR", false, "Slot %u radio uninhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_CHECK_ACK) {
- ::ActivityLog("DMR", false, "Slot %u radio check response from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT_ACK) {
- ::ActivityLog("DMR", false, "Slot %u radio inhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT_ACK) {
- ::ActivityLog("DMR", false, "Slot %u radio uninhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId);
- }
- break;
- case CSBKO_PRECCSBK:
- if (m_verbose) {
- LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_PRECCSBK (%s Preamble CSBK), toFollow = %u, src = %u, dst = %s%u",
- m_slot->m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), srcId, gi ? "TG " : "", dstId);
- }
- break;
- default:
- LogWarning(LOG_NET, "DMR Slot %u, DT_CSBK, unhandled network CSBK, csbko = $%02X, fid = $%02X", m_slot->m_slotNo, csbko, csbk.getFID());
- break;
- }
- }
+ m_slot->writeEndNet();
}
else if (dataType == DT_DATA_HEADER) {
if (m_slot->m_netState == RS_NET_DATA)
@@ -999,12 +408,12 @@ void DataPacket::processNetwork(const data::Data& dmrData)
if (m_slot->m_netFrames == 0U) {
::ActivityLog("DMR", false, "Slot %u ended network data transmission", m_slot->m_slotNo);
- writeEndNet();
+ m_slot->writeEndNet();
}
}
else if (dataType == DT_RATE_12_DATA || dataType == DT_RATE_34_DATA || dataType == DT_RATE_1_DATA) {
if (m_slot->m_netState != RS_NET_DATA || m_slot->m_netFrames == 0U) {
- writeEndNet();
+ m_slot->writeEndNet();
return;
}
@@ -1070,7 +479,7 @@ void DataPacket::processNetwork(const data::Data& dmrData)
}
LogMessage(LOG_NET, "DMR Slot %u, DT_RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo);
- writeEndNet();
+ m_slot->writeEndNet();
}
}
else {
@@ -1089,19 +498,15 @@ void DataPacket::processNetwork(const data::Data& dmrData)
/// Instance of the BaseNetwork class.
///
///
-///
/// Flag indicating whether DMR debug is enabled.
/// Flag indicating whether DMR verbose logging is enabled.
-DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) :
+DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) :
m_slot(slot),
- m_rfLC(NULL),
- m_netLC(NULL),
m_pduUserData(NULL),
m_pduDataOffset(0U),
m_lastRejectId(0U),
m_dumpDataPacket(dumpDataPacket),
m_repeatDataPacket(repeatDataPacket),
- m_dumpCSBKData(dumpCSBKData),
m_verbose(verbose),
m_debug(debug)
{
@@ -1116,113 +521,3 @@ DataPacket::~DataPacket()
{
delete[] m_pduUserData;
}
-
-///
-/// Helper to write RF end of frame data.
-///
-///
-void DataPacket::writeEndRF(bool writeEnd)
-{
- m_slot->m_rfState = RS_RF_LISTENING;
-
- if (m_slot->m_netState == RS_NET_IDLE) {
- m_slot->setShortLC(m_slot->m_slotNo, 0U);
- }
-
- if (writeEnd) {
- if (m_slot->m_netState == RS_NET_IDLE && m_slot->m_duplex && !m_slot->m_rfTimeout) {
- // Create a dummy start end frame
- uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
-
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- lc::FullLC fullLC;
- fullLC.encode(*m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC);
-
- SlotType slotType;
- slotType.setColorCode(m_slot->m_colorCode);
- slotType.setDataType(DT_TERMINATOR_WITH_LC);
- slotType.encode(data + 2U);
-
- data[0U] = TAG_EOT;
- data[1U] = 0x00U;
-
- for (uint32_t i = 0U; i < m_slot->m_hangCount; i++)
- m_slot->writeQueueRF(data);
- }
- }
-
- m_pduDataOffset = 0U;
-
- if (m_slot->m_network != NULL)
- m_slot->m_network->resetDMR(m_slot->m_slotNo);
-
- m_slot->m_rfTimeoutTimer.stop();
- m_slot->m_rfTimeout = false;
-
- m_slot->m_rfFrames = 0U;
- m_slot->m_rfErrs = 0U;
- m_slot->m_rfBits = 1U;
-
- delete m_rfLC;
- m_rfLC = NULL;
-}
-
-///
-/// Helper to write network end of frame data.
-///
-///
-void DataPacket::writeEndNet(bool writeEnd)
-{
- m_slot->m_netState = RS_NET_IDLE;
-
- m_slot->setShortLC(m_slot->m_slotNo, 0U);
-
- m_slot->m_voice->m_lastFrameValid = false;
-
- if (writeEnd && !m_slot->m_netTimeout) {
- // Create a dummy start end frame
- uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
-
- Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
-
- lc::FullLC fullLC;
- fullLC.encode(*m_netLC, data + 2U, DT_TERMINATOR_WITH_LC);
-
- SlotType slotType;
- slotType.setColorCode(m_slot->m_colorCode);
- slotType.setDataType(DT_TERMINATOR_WITH_LC);
- slotType.encode(data + 2U);
-
- data[0U] = TAG_EOT;
- data[1U] = 0x00U;
-
- if (m_slot->m_duplex) {
- for (uint32_t i = 0U; i < m_slot->m_hangCount; i++)
- m_slot->writeQueueNet(data);
- }
- else {
- for (uint32_t i = 0U; i < 3U; i++)
- m_slot->writeQueueNet(data);
- }
- }
-
- m_pduDataOffset = 0U;
-
- if (m_slot->m_network != NULL)
- m_slot->m_network->resetDMR(m_slot->m_slotNo);
-
- m_slot->m_networkWatchdog.stop();
- m_slot->m_netTimeoutTimer.stop();
- m_slot->m_packetTimer.stop();
- m_slot->m_netTimeout = false;
-
- m_slot->m_netFrames = 0U;
- m_slot->m_netLost = 0U;
-
- m_slot->m_netErrs = 0U;
- m_slot->m_netBits = 1U;
-
- delete m_netLC;
- m_netLC = NULL;
-}
diff --git a/dmr/DataPacket.h b/dmr/DataPacket.h
index 95973c79..436f08d3 100644
--- a/dmr/DataPacket.h
+++ b/dmr/DataPacket.h
@@ -53,6 +53,7 @@ namespace dmr
// Class Prototypes
// ---------------------------------------------------------------------------
class HOST_SW_API VoicePacket;
+ class HOST_SW_API ControlPacket;
class HOST_SW_API Slot;
// ---------------------------------------------------------------------------
@@ -69,12 +70,10 @@ namespace dmr
private:
friend class VoicePacket;
+ friend class ControlPacket;
friend class Slot;
Slot* m_slot;
- lc::LC* m_rfLC;
- lc::LC* m_netLC;
-
uint8_t* m_pduUserData;
uint32_t m_pduDataOffset;
uint32_t m_lastRejectId;
@@ -82,19 +81,13 @@ namespace dmr
bool m_dumpDataPacket;
bool m_repeatDataPacket;
- bool m_dumpCSBKData;
bool m_verbose;
bool m_debug;
/// Initializes a new instance of the DataPacket class.
- DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose);
+ DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose);
/// Finalizes a instance of the DataPacket class.
~DataPacket();
-
- /// Helper to write RF end of frame data.
- void writeEndRF(bool writeEnd = false);
- /// Helper to write network end of frame data.
- void writeEndNet(bool writeEnd = false);
};
} // namespace dmr
diff --git a/dmr/Slot.cpp b/dmr/Slot.cpp
index b821639d..8320c5e9 100644
--- a/dmr/Slot.cpp
+++ b/dmr/Slot.cpp
@@ -112,7 +112,9 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_rfLastDstId(0U),
m_netState(RS_NET_IDLE),
m_netLastDstId(0U),
+ m_rfLC(NULL),
m_rfSeqNo(0U),
+ m_netLC(NULL),
m_networkWatchdog(1000U, 0U, 1500U),
m_rfTimeoutTimer(1000U, timeout),
m_rfTGHang(1000U, tgHang),
@@ -142,7 +144,8 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_interval.start();
m_voice = new VoicePacket(this, m_network, m_embeddedLCOnly, m_dumpTAData, debug, verbose);
- m_data = new DataPacket(this, m_network, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose);
+ m_data = new DataPacket(this, m_network, dumpDataPacket, repeatDataPacket, debug, verbose);
+ m_control = new ControlPacket(this, m_network, dumpCSBKData, debug, verbose);
}
///
@@ -152,6 +155,7 @@ Slot::~Slot()
{
delete m_voice;
delete m_data;
+ delete m_control;
}
///
@@ -180,18 +184,18 @@ bool Slot::processFrame(uint8_t *data, uint32_t len)
m_slotNo, m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rfTimeout) {
- m_data->writeEndRF();
+ writeEndRF();
return false;
}
else {
- m_data->writeEndRF(true);
+ writeEndRF(true);
return true;
}
}
if (data[0U] == TAG_LOST && m_rfState == RS_RF_DATA) {
::ActivityLog("DMR", true, "Slot %u, RF data transmission lost", m_slotNo);
- m_data->writeEndRF();
+ writeEndRF();
return false;
}
@@ -255,7 +259,23 @@ bool Slot::processFrame(uint8_t *data, uint32_t len)
m_rfTGHang.start();
if (dataSync) {
- return m_data->process(data, len);
+ uint8_t dataType = data[1U] & 0x0FU;
+
+ switch (dataType)
+ {
+ case DT_CSBK:
+ return m_control->process(data, len);
+ case DT_VOICE_LC_HEADER:
+ case DT_VOICE_PI_HEADER:
+ return m_voice->process(data, len);
+ case DT_TERMINATOR_WITH_LC:
+ case DT_DATA_HEADER:
+ case DT_RATE_12_DATA:
+ case DT_RATE_34_DATA:
+ case DT_RATE_1_DATA:
+ default:
+ return m_data->process(data, len);
+ }
}
return m_voice->process(data, len);
@@ -310,20 +330,23 @@ void Slot::processNetwork(const data::Data& dmrData)
switch (dataType)
{
+ case DT_CSBK:
+ m_control->processNetwork(dmrData);
+ break;
case DT_VOICE_LC_HEADER:
case DT_VOICE_PI_HEADER:
+ case DT_VOICE_SYNC:
+ case DT_VOICE:
+ m_voice->processNetwork(dmrData);
+ break;
case DT_TERMINATOR_WITH_LC:
case DT_DATA_HEADER:
- case DT_CSBK:
case DT_RATE_12_DATA:
case DT_RATE_34_DATA:
case DT_RATE_1_DATA:
+ default:
m_data->processNetwork(dmrData);
break;
- case DT_VOICE_SYNC:
- case DT_VOICE:
- default:
- m_voice->processNetwork(dmrData);
}
}
@@ -380,11 +403,11 @@ void Slot::clock()
m_netFrames += 1U;
::ActivityLog("DMR", false, "Slot %u network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%",
m_slotNo, float(m_netFrames) / 16.667F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits));
- m_data->writeEndNet(true);
+ writeEndNet(true);
}
else {
::ActivityLog("DMR", false, "Slot %u network watchdog has expired", m_slotNo);
- m_data->writeEndNet();
+ writeEndNet();
}
}
}
@@ -556,9 +579,9 @@ void Slot::writeQueueNet(const uint8_t *data)
void Slot::writeNetworkRF(const uint8_t* data, uint8_t dataType, uint8_t errors)
{
assert(data != NULL);
- assert(m_data->m_rfLC != NULL);
+ assert(m_rfLC != NULL);
- writeNetworkRF(data, dataType, m_data->m_rfLC->getFLCO(), m_data->m_rfLC->getSrcId(), m_data->m_rfLC->getDstId(), errors);
+ writeNetworkRF(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), errors);
}
///
@@ -820,6 +843,116 @@ void Slot::writeRF_TSCC_Bcast_Sys_Parm()
writeQueueRF(data);
}
+///
+/// Helper to write RF end of frame data.
+///
+///
+void Slot::writeEndRF(bool writeEnd)
+{
+ m_rfState = RS_RF_LISTENING;
+
+ if (m_netState == RS_NET_IDLE) {
+ setShortLC(m_slotNo, 0U);
+ }
+
+ if (writeEnd) {
+ if (m_netState == RS_NET_IDLE && m_duplex && !m_rfTimeout) {
+ // Create a dummy start end frame
+ uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
+
+ Sync::addDMRDataSync(data + 2U, m_duplex);
+
+ lc::FullLC fullLC;
+ fullLC.encode(*m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC);
+
+ SlotType slotType;
+ slotType.setColorCode(m_colorCode);
+ slotType.setDataType(DT_TERMINATOR_WITH_LC);
+ slotType.encode(data + 2U);
+
+ data[0U] = TAG_EOT;
+ data[1U] = 0x00U;
+
+ for (uint32_t i = 0U; i < m_hangCount; i++)
+ writeQueueRF(data);
+ }
+ }
+
+ m_data->m_pduDataOffset = 0U;
+
+ if (m_network != NULL)
+ m_network->resetDMR(m_slotNo);
+
+ m_rfTimeoutTimer.stop();
+ m_rfTimeout = false;
+
+ m_rfFrames = 0U;
+ m_rfErrs = 0U;
+ m_rfBits = 1U;
+
+ delete m_rfLC;
+ m_rfLC = NULL;
+}
+
+///
+/// Helper to write network end of frame data.
+///
+///
+void Slot::writeEndNet(bool writeEnd)
+{
+ m_netState = RS_NET_IDLE;
+
+ setShortLC(m_slotNo, 0U);
+
+ m_voice->m_lastFrameValid = false;
+
+ if (writeEnd && !m_netTimeout) {
+ // Create a dummy start end frame
+ uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
+
+ Sync::addDMRDataSync(data + 2U, m_duplex);
+
+ lc::FullLC fullLC;
+ fullLC.encode(*m_netLC, data + 2U, DT_TERMINATOR_WITH_LC);
+
+ SlotType slotType;
+ slotType.setColorCode(m_colorCode);
+ slotType.setDataType(DT_TERMINATOR_WITH_LC);
+ slotType.encode(data + 2U);
+
+ data[0U] = TAG_EOT;
+ data[1U] = 0x00U;
+
+ if (m_duplex) {
+ for (uint32_t i = 0U; i < m_hangCount; i++)
+ writeQueueNet(data);
+ }
+ else {
+ for (uint32_t i = 0U; i < 3U; i++)
+ writeQueueNet(data);
+ }
+ }
+
+ m_data->m_pduDataOffset = 0U;
+
+ if (m_network != NULL)
+ m_network->resetDMR(m_slotNo);
+
+ m_networkWatchdog.stop();
+ m_netTimeoutTimer.stop();
+ m_packetTimer.stop();
+ m_netTimeout = false;
+
+ m_netFrames = 0U;
+ m_netLost = 0U;
+
+ m_netErrs = 0U;
+ m_netBits = 1U;
+
+ delete m_netLC;
+ m_netLC = NULL;
+}
+
///
///
///
diff --git a/dmr/Slot.h b/dmr/Slot.h
index ea4b5c3f..f1237079 100644
--- a/dmr/Slot.h
+++ b/dmr/Slot.h
@@ -34,6 +34,7 @@
#include "Defines.h"
#include "dmr/Control.h"
#include "dmr/SiteData.h"
+#include "dmr/ControlPacket.h"
#include "dmr/DataPacket.h"
#include "dmr/VoicePacket.h"
#include "modem/Modem.h"
@@ -56,6 +57,7 @@ namespace dmr
class HOST_SW_API Control;
class HOST_SW_API VoicePacket;
class HOST_SW_API DataPacket;
+ class HOST_SW_API ControlPacket;
// ---------------------------------------------------------------------------
// Class Declaration
@@ -94,6 +96,8 @@ namespace dmr
VoicePacket* m_voice;
friend class DataPacket;
DataPacket* m_data;
+ friend class ControlPacket;
+ ControlPacket* m_control;
uint32_t m_slotNo;
@@ -104,8 +108,12 @@ namespace dmr
RPT_NET_STATE m_netState;
uint32_t m_netLastDstId;
+ lc::LC* m_rfLC;
+
uint8_t m_rfSeqNo;
+ lc::LC* m_netLC;
+
Timer m_networkWatchdog;
Timer m_rfTimeoutTimer;
Timer m_rfTGHang;
@@ -197,6 +205,11 @@ namespace dmr
/// Helper to write a TSCC Sys_Parm broadcast packet on the RF interface.
void writeRF_TSCC_Bcast_Sys_Parm();
+ /// Helper to write RF end of frame data.
+ void writeEndRF(bool writeEnd = false);
+ /// Helper to write network end of frame data.
+ void writeEndNet(bool writeEnd = false);
+
///
static void setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco = FLCO_GROUP, bool voice = true);
///
diff --git a/dmr/VoicePacket.cpp b/dmr/VoicePacket.cpp
index 32b6eb0e..35a376a1 100644
--- a/dmr/VoicePacket.cpp
+++ b/dmr/VoicePacket.cpp
@@ -56,6 +56,14 @@ using namespace dmr;
return false; \
}
+#define CHECK_TG_HANG_DELLC(_DST_ID) \
+ if (m_slot->m_rfLastDstId != 0U) { \
+ if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \
+ delete lc; \
+ return; \
+ } \
+ }
+
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
@@ -69,8 +77,167 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
{
assert(data != NULL);
+ bool dataSync = (data[1U] & DMR_SYNC_DATA) == DMR_SYNC_DATA;
bool voiceSync = (data[1U] & DMR_SYNC_VOICE) == DMR_SYNC_VOICE;
+ if (dataSync) {
+ uint8_t dataType = data[1U] & 0x0FU;
+
+ SlotType slotType;
+ slotType.setColorCode(m_slot->m_colorCode);
+ slotType.setDataType(dataType);
+
+ if (dataType == DT_VOICE_LC_HEADER) {
+ if (m_slot->m_rfState == RS_RF_AUDIO)
+ return true;
+
+ lc::FullLC fullLC;
+ lc::LC * lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER);
+ if (lc == NULL)
+ return false;
+
+ uint32_t srcId = lc->getSrcId();
+ uint32_t dstId = lc->getDstId();
+ uint8_t flco = lc->getFLCO();
+
+ CHECK_TRAFFIC_COLLISION_DELLC(dstId);
+
+ // validate source RID
+ if (!acl::AccessControl::validateSrcId(srcId)) {
+ if (m_slot->m_data->m_lastRejectId == 0U || m_slot->m_data->m_lastRejectId == srcId) {
+ LogWarning(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId);
+ ::ActivityLog("DMR", true, "Slot %u RF voice rejection from %u to %s%u ", m_slot->m_slotNo, srcId, flco == FLCO_GROUP ? "TG " : "", dstId);
+ }
+
+ m_slot->m_rfLastDstId = 0U;
+ m_slot->m_rfTGHang.stop();
+
+ delete lc;
+ return false;
+ }
+
+ // validate target TID, if the target is a talkgroup
+ if (flco == FLCO_GROUP) {
+ if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) {
+ if (m_slot->m_data->m_lastRejectId == 0U || m_slot->m_data->m_lastRejectId == dstId) {
+ LogWarning(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId);
+ ::ActivityLog("DMR", true, "Slot %u RF voice rejection from %u to TG %u ", m_slot->m_slotNo, srcId, dstId);
+ }
+
+ m_slot->m_rfLastDstId = 0U;
+ m_slot->m_rfTGHang.stop();
+
+ delete lc;
+ return false;
+ }
+ }
+
+ m_slot->m_data->m_lastRejectId = 0U;
+
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF());
+ }
+
+ uint8_t fid = lc->getFID();
+
+ // NOTE: this is fiddly -- on Motorola a FID of 0x10 indicates a SU has transmitted with Enhanced Privacy enabled -- this might change
+ // and is not exact science!
+ bool encrypted = (fid & 0x10U) == 0x10U;
+
+ m_slot->m_rfLC = lc;
+
+ // The standby LC data
+ m_slot->m_voice->m_rfEmbeddedLC.setLC(*m_slot->m_rfLC);
+ m_slot->m_voice->m_rfEmbeddedData[0U].setLC(*m_slot->m_rfLC);
+ m_slot->m_voice->m_rfEmbeddedData[1U].setLC(*m_slot->m_rfLC);
+
+ // Regenerate the LC data
+ fullLC.encode(*m_slot->m_rfLC, data + 2U, DT_VOICE_LC_HEADER);
+
+ // Regenerate the Slot Type
+ slotType.encode(data + 2U);
+
+ // Convert the Data Sync to be from the BS or MS as needed
+ Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
+
+ data[0U] = TAG_DATA;
+ data[1U] = 0x00U;
+
+ m_slot->m_rfTimeoutTimer.start();
+ m_slot->m_rfTimeout = false;
+
+ m_slot->m_rfFrames = 0U;
+ m_slot->m_rfSeqNo = 0U;
+ m_slot->m_rfBits = 1U;
+ m_slot->m_rfErrs = 0U;
+
+ m_slot->m_voice->m_rfEmbeddedReadN = 0U;
+ m_slot->m_voice->m_rfEmbeddedWriteN = 1U;
+ m_slot->m_voice->m_rfTalkerId = TALKER_ID_NONE;
+
+ m_slot->m_minRSSI = m_slot->m_rssi;
+ m_slot->m_maxRSSI = m_slot->m_rssi;
+ m_slot->m_aveRSSI = m_slot->m_rssi;
+ m_slot->m_rssiCount = 1U;
+
+ if (m_slot->m_duplex) {
+ m_slot->m_queue.clear();
+ m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo);
+
+ for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
+ m_slot->writeQueueRF(data);
+ }
+
+ m_slot->writeNetworkRF(data, DT_VOICE_LC_HEADER);
+
+ m_slot->m_rfState = RS_RF_AUDIO;
+ m_slot->m_rfLastDstId = dstId;
+
+ if (m_slot->m_netState == RS_NET_IDLE) {
+ m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true);
+ }
+
+ if (m_debug) {
+ Utils::dump(2U, "!!! *TX DMR Frame - DT_VOICE_LC_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES);
+ }
+
+ ::ActivityLog("DMR", true, "Slot %u RF %svoice header from %u to %s%u", m_slot->m_slotNo, encrypted ? "encrypted " : "", srcId, flco == FLCO_GROUP ? "TG " : "", dstId);
+ return true;
+ }
+ else if (dataType == DT_VOICE_PI_HEADER) {
+ if (m_slot->m_rfState != RS_RF_AUDIO)
+ return false;
+
+ // Regenerate the Slot Type
+ slotType.encode(data + 2U);
+
+ // Convert the Data Sync to be from the BS or MS as needed
+ Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
+
+ // Regenerate the payload and the BPTC (196,96) FEC
+ edac::BPTC19696 bptc;
+ uint8_t payload[12U];
+ bptc.decode(data + 2U, payload);
+ bptc.encode(payload, data + 2U);
+
+ data[0U] = TAG_DATA;
+ data[1U] = 0x00U;
+
+ if (m_slot->m_duplex)
+ m_slot->writeQueueRF(data);
+
+ m_slot->writeNetworkRF(data, DT_VOICE_PI_HEADER);
+
+ if (m_debug) {
+ Utils::dump(2U, "!!! *TX DMR Frame - DT_VOICE_PI_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
if (voiceSync) {
if (m_slot->m_rfState == RS_RF_AUDIO) {
m_lastRfN = 0U;
@@ -79,7 +246,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
Sync::addDMRAudioSync(data + 2U, m_slot->m_duplex);
uint32_t errors = 0U;
- uint8_t fid = m_slot->m_data->m_rfLC->getFID();
+ uint8_t fid = m_slot->m_rfLC->getFID();
if (fid == FID_ETSI || fid == FID_DMRA) {
errors = m_fec.regenerateDMR(data + 2U);
if (m_verbose) {
@@ -94,7 +261,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
m_slot->m_rfFrames++;
m_slot->m_rfTGHang.start();
- m_slot->m_rfLastDstId = m_slot->m_data->m_rfLC->getDstId();
+ m_slot->m_rfLastDstId = m_slot->m_rfLC->getDstId();
m_rfEmbeddedReadN = (m_rfEmbeddedReadN + 1U) % 2U;
m_rfEmbeddedWriteN = (m_rfEmbeddedWriteN + 1U) % 2U;
@@ -132,7 +299,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
m_lastRfN = m_rfN;
uint32_t errors = 0U;
- uint8_t fid = m_slot->m_data->m_rfLC->getFID();
+ uint8_t fid = m_slot->m_rfLC->getFID();
if (fid == FID_ETSI || fid == FID_DMRA) {
errors = m_fec.regenerateDMR(data + 2U);
if (m_verbose) {
@@ -147,7 +314,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
m_slot->m_rfFrames++;
m_slot->m_rfTGHang.start();
- m_slot->m_rfLastDstId = m_slot->m_data->m_rfLC->getDstId();
+ m_slot->m_rfLastDstId = m_slot->m_rfLC->getDstId();
// Get the LCSS from the EMB
data::EMB emb;
@@ -177,7 +344,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
}
if (m_verbose) {
- logGPSPosition(m_slot->m_data->m_rfLC->getSrcId(), data);
+ logGPSPosition(m_slot->m_rfLC->getSrcId(), data);
}
break;
@@ -301,12 +468,12 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
}
}
- m_slot->m_data->m_rfLC = lc;
+ m_slot->m_rfLC = lc;
// The standby LC data
- m_rfEmbeddedLC.setLC(*m_slot->m_data->m_rfLC);
- m_rfEmbeddedData[0U].setLC(*m_slot->m_data->m_rfLC);
- m_rfEmbeddedData[1U].setLC(*m_slot->m_data->m_rfLC);
+ m_rfEmbeddedLC.setLC(*m_slot->m_rfLC);
+ m_rfEmbeddedData[0U].setLC(*m_slot->m_rfLC);
+ m_rfEmbeddedData[1U].setLC(*m_slot->m_rfLC);
// Create a dummy start frame to replace the received frame
uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U];
@@ -314,7 +481,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
Sync::addDMRDataSync(start + 2U, m_slot->m_duplex);
lc::FullLC fullLC;
- fullLC.encode(*m_slot->m_data->m_rfLC, start + 2U, DT_VOICE_LC_HEADER);
+ fullLC.encode(*m_slot->m_rfLC, start + 2U, DT_VOICE_LC_HEADER);
SlotType slotType;
slotType.setColorCode(m_slot->m_colorCode);
@@ -364,7 +531,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
// Send the original audio frame out
uint32_t errors = 0U;
- uint8_t fid = m_slot->m_data->m_rfLC->getFID();
+ uint8_t fid = m_slot->m_rfLC->getFID();
if (fid == FID_ETSI || fid == FID_DMRA) {
errors = m_fec.regenerateDMR(data + 2U);
if (m_verbose) {
@@ -415,19 +582,196 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
dmrData.getData(data + 2U);
- if (dataType == DT_VOICE_SYNC) {
+ if (dataType == DT_VOICE_LC_HEADER) {
+ if (m_slot->m_netState == RS_NET_AUDIO)
+ return;
+
+ lc::FullLC fullLC;
+ lc::LC * lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER);
+ if (lc == NULL) {
+ LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, bad LC received from the network, replacing", m_slot->m_slotNo);
+ lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId());
+ }
+
+ uint32_t srcId = lc->getSrcId();
+ uint32_t dstId = lc->getDstId();
+ uint8_t flco = lc->getFLCO();
+
+ CHECK_TG_HANG_DELLC(dstId);
+
+ if (dstId != dmrData.getDstId() || srcId != dmrData.getSrcId() || flco != dmrData.getFLCO())
+ LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, header doesn't match the DMR RF header: %u->%s%u %u->%s%u", m_slot->m_slotNo,
+ dmrData.getSrcId(), dmrData.getFLCO() == FLCO_GROUP ? "TG" : "", dmrData.getDstId(),
+ srcId, flco == FLCO_GROUP ? "TG" : "", dstId);
+
+ if (m_verbose) {
+ LogMessage(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF());
+ }
+
+ m_slot->m_netLC = lc;
+
+ // The standby LC data
+ m_slot->m_voice->m_netEmbeddedLC.setLC(*m_slot->m_netLC);
+ m_slot->m_voice->m_netEmbeddedData[0U].setLC(*m_slot->m_netLC);
+ m_slot->m_voice->m_netEmbeddedData[1U].setLC(*m_slot->m_netLC);
+
+ // Regenerate the LC data
+ fullLC.encode(*m_slot->m_netLC, data + 2U, DT_VOICE_LC_HEADER);
+
+ // Regenerate the Slot Type
+ SlotType slotType;
+ slotType.setColorCode(m_slot->m_colorCode);
+ slotType.setDataType(DT_VOICE_LC_HEADER);
+ slotType.encode(data + 2U);
+
+ // Convert the Data Sync to be from the BS or MS as needed
+ Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
+
+ data[0U] = TAG_DATA;
+ data[1U] = 0x00U;
+
+ m_slot->m_voice->m_lastFrameValid = false;
+
+ m_slot->m_netTimeoutTimer.start();
+ m_slot->m_netTimeout = false;
+
+ m_slot->m_netFrames = 0U;
+ m_slot->m_netLost = 0U;
+ m_slot->m_netBits = 1U;
+ m_slot->m_netErrs = 0U;
+
+ m_slot->m_voice->m_netEmbeddedReadN = 0U;
+ m_slot->m_voice->m_netEmbeddedWriteN = 1U;
+ m_slot->m_voice->m_netTalkerId = TALKER_ID_NONE;
+
+ if (m_slot->m_duplex) {
+ m_slot->m_queue.clear();
+ m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo);
+ }
+
+ for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++)
+ m_slot->writeQueueNet(m_slot->m_idle);
+
+ if (m_slot->m_duplex) {
+ for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
+ m_slot->writeQueueNet(data);
+ }
+ else {
+ for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++)
+ m_slot->writeQueueNet(data);
+ }
+
+ m_slot->m_netState = RS_NET_AUDIO;
+ m_slot->m_netLastDstId = dstId;
+
+ m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true);
+
+ if (m_debug) {
+ Utils::dump(2U, "!!! *TX DMR Network Frame - DT_VOICE_LC_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES);
+ }
+
+ ::ActivityLog("DMR", false, "Slot %u network voice header from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO_GROUP ? "TG " : "", dstId);
+ }
+ else if (dataType == DT_VOICE_PI_HEADER) {
+ if (m_slot->m_netState != RS_NET_AUDIO) {
+ lc::LC* lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId());
+
+ uint32_t srcId = lc->getSrcId();
+ uint32_t dstId = lc->getDstId();
+
+ CHECK_TG_HANG_DELLC(dstId);
+
+ m_slot->m_netLC = lc;
+
+ m_slot->m_voice->m_lastFrameValid = false;
+
+ m_slot->m_netTimeoutTimer.start();
+ m_slot->m_netTimeout = false;
+
+ if (m_slot->m_duplex) {
+ m_slot->m_queue.clear();
+ m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo);
+ }
+
+ for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++)
+ m_slot->writeQueueNet(m_slot->m_idle);
+
+ // Create a dummy start frame
+ uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U];
+
+ Sync::addDMRDataSync(start + 2U, m_slot->m_duplex);
+
+ lc::FullLC fullLC;
+ fullLC.encode(*m_slot->m_netLC, start + 2U, DT_VOICE_LC_HEADER);
+
+ SlotType slotType;
+ slotType.setColorCode(m_slot->m_colorCode);
+ slotType.setDataType(DT_VOICE_LC_HEADER);
+ slotType.encode(start + 2U);
+
+ start[0U] = TAG_DATA;
+ start[1U] = 0x00U;
+
+ if (m_slot->m_duplex) {
+ for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
+ m_slot->writeQueueRF(start);
+ }
+ else {
+ for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++)
+ m_slot->writeQueueRF(start);
+ }
+
+ m_slot->m_netFrames = 0U;
+ m_slot->m_netLost = 0U;
+ m_slot->m_netBits = 1U;
+ m_slot->m_netErrs = 0U;
+
+ m_slot->m_netState = RS_NET_AUDIO;
+ m_slot->m_netLastDstId = dstId;
+
+ m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_netLC->getFLCO(), true);
+
+ ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u",
+ m_slot->m_slotNo, srcId, m_slot->m_netLC->getFLCO() == FLCO_GROUP ? "TG " : "", dstId);
+ }
+
+ // Regenerate the Slot Type
+ SlotType slotType;
+ slotType.setColorCode(m_slot->m_colorCode);
+ slotType.setDataType(DT_VOICE_PI_HEADER);
+ slotType.encode(data + 2U);
+
+ // Convert the Data Sync to be from the BS or MS as needed
+ Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
+
+ // Regenerate the payload and the BPTC (196,96) FEC
+ edac::BPTC19696 bptc;
+ uint8_t payload[12U];
+ bptc.decode(data + 2U, payload);
+ bptc.encode(payload, data + 2U);
+
+ data[0U] = TAG_DATA;
+ data[1U] = 0x00U;
+
+ m_slot->writeQueueNet(data);
+
+ if (m_debug) {
+ Utils::dump(2U, "!!! *TX DMR Network Frame - DT_VOICE_PI_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES);
+ }
+ }
+ else if (dataType == DT_VOICE_SYNC) {
if (m_slot->m_netState == RS_NET_IDLE) {
lc::LC* lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId());
uint32_t dstId = lc->getDstId();
uint32_t srcId = lc->getSrcId();
- m_slot->m_data->m_netLC = lc;
+ m_slot->m_netLC = lc;
// The standby LC data
- m_netEmbeddedLC.setLC(*m_slot->m_data->m_netLC);
- m_netEmbeddedData[0U].setLC(*m_slot->m_data->m_netLC);
- m_netEmbeddedData[1U].setLC(*m_slot->m_data->m_netLC);
+ m_netEmbeddedLC.setLC(*m_slot->m_netLC);
+ m_netEmbeddedData[0U].setLC(*m_slot->m_netLC);
+ m_netEmbeddedData[1U].setLC(*m_slot->m_netLC);
m_lastFrameValid = false;
@@ -448,7 +792,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
Sync::addDMRDataSync(start + 2U, m_slot->m_duplex);
lc::FullLC fullLC;
- fullLC.encode(*m_slot->m_data->m_netLC, start + 2U, DT_VOICE_LC_HEADER);
+ fullLC.encode(*m_slot->m_netLC, start + 2U, DT_VOICE_LC_HEADER);
SlotType slotType;
slotType.setColorCode(m_slot->m_colorCode);
@@ -479,14 +823,14 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
m_slot->m_netState = RS_NET_AUDIO;
m_slot->m_netLastDstId = dstId;
- m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_data->m_netLC->getFLCO(), true);
+ m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_netLC->getFLCO(), true);
::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u",
- m_slot->m_slotNo, srcId, m_slot->m_data->m_netLC->getFLCO() == FLCO_GROUP ? "TG " : "", dstId);
+ m_slot->m_slotNo, srcId, m_slot->m_netLC->getFLCO() == FLCO_GROUP ? "TG " : "", dstId);
}
if (m_slot->m_netState == RS_NET_AUDIO) {
- uint8_t fid = m_slot->m_data->m_netLC->getFID();
+ uint8_t fid = m_slot->m_netLC->getFID();
if (fid == FID_ETSI || fid == FID_DMRA) {
m_slot->m_netErrs += m_fec.regenerateDMR(data + 2U);
if (m_verbose) {
@@ -531,7 +875,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
if (m_slot->m_netState != RS_NET_AUDIO)
return;
- uint8_t fid = m_slot->m_data->m_netLC->getFID();
+ uint8_t fid = m_slot->m_netLC->getFID();
if (fid == FID_ETSI || fid == FID_DMRA) {
m_slot->m_netErrs += m_fec.regenerateDMR(data + 2U);
if (m_verbose) {
@@ -567,7 +911,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
Utils::dump(2U, text, data, 9U);
}
- logGPSPosition(m_slot->m_data->m_netLC->getSrcId(), data);
+ logGPSPosition(m_slot->m_netLC->getSrcId(), data);
break;
case FLCO_TALKER_ALIAS_HEADER:
if (!(m_netTalkerId & TALKER_ID_HEADER)) {
@@ -822,7 +1166,7 @@ void VoicePacket::insertSilence(uint32_t count)
uint8_t n = (m_netN + 1U) % 6U;
- uint8_t fid = m_slot->m_data->m_netLC->getFID();
+ uint8_t fid = m_slot->m_netLC->getFID();
data::EMB emb;
emb.setColorCode(m_slot->m_colorCode);