split DMR CSBK handling into its own packet handler class; cleanup DMR data and voice packet handlers (processing for certain data packets wasn't exactly right); move some common variables into the main Slot class;

pull/1/head
Bryan Biedenkapp 5 years ago
parent b5090e02f2
commit f095065451

@ -155,6 +155,7 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="dmr\acl\AccessControl.h" /> <ClInclude Include="dmr\acl\AccessControl.h" />
<ClInclude Include="dmr\ControlPacket.h" />
<ClInclude Include="dmr\data\Data.h" /> <ClInclude Include="dmr\data\Data.h" />
<ClInclude Include="dmr\data\DataHeader.h" /> <ClInclude Include="dmr\data\DataHeader.h" />
<ClInclude Include="dmr\data\EMB.h" /> <ClInclude Include="dmr\data\EMB.h" />
@ -230,6 +231,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="dmr\acl\AccessControl.cpp" /> <ClCompile Include="dmr\acl\AccessControl.cpp" />
<ClCompile Include="dmr\ControlPacket.cpp" />
<ClCompile Include="dmr\data\Data.cpp" /> <ClCompile Include="dmr\data\Data.cpp" />
<ClCompile Include="dmr\data\DataHeader.cpp" /> <ClCompile Include="dmr\data\DataHeader.cpp" />
<ClCompile Include="dmr\data\EMB.cpp" /> <ClCompile Include="dmr\data\EMB.cpp" />

@ -332,6 +332,9 @@
<ClInclude Include="dmr\SiteData.h"> <ClInclude Include="dmr\SiteData.h">
<Filter>Header Files\dmr</Filter> <Filter>Header Files\dmr</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="dmr\ControlPacket.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Log.cpp"> <ClCompile Include="Log.cpp">
@ -532,6 +535,9 @@
<ClCompile Include="p25\data\DataRspHeader.cpp"> <ClCompile Include="p25\data\DataRspHeader.cpp">
<Filter>Source Files\p25\data</Filter> <Filter>Source Files\p25\data</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="dmr\ControlPacket.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Makefile" /> <None Include="Makefile" />

@ -27,6 +27,7 @@ OBJECTS = \
dmr/lc/LC.o \ dmr/lc/LC.o \
dmr/lc/ShortLC.o \ dmr/lc/ShortLC.o \
dmr/Control.o \ dmr/Control.o \
dmr/ControlPacket.o \
dmr/DataPacket.o \ dmr/DataPacket.o \
dmr/Slot.o \ dmr/Slot.o \
dmr/SlotType.o \ dmr/SlotType.o \

@ -27,6 +27,7 @@ OBJECTS = \
dmr/lc/LC.o \ dmr/lc/LC.o \
dmr/lc/ShortLC.o \ dmr/lc/ShortLC.o \
dmr/Control.o \ dmr/Control.o \
dmr/ControlPacket.o \
dmr/DataPacket.o \ dmr/DataPacket.o \
dmr/Slot.o \ dmr/Slot.o \
dmr/SlotType.o \ dmr/SlotType.o \

@ -27,6 +27,7 @@ OBJECTS = \
dmr/lc/LC.o \ dmr/lc/LC.o \
dmr/lc/ShortLC.o \ dmr/lc/ShortLC.o \
dmr/Control.o \ dmr/Control.o \
dmr/ControlPacket.o \
dmr/DataPacket.o \ dmr/DataPacket.o \
dmr/Slot.o \ dmr/Slot.o \
dmr/SlotType.o \ dmr/SlotType.o \

@ -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 <cassert>
#include <ctime>
#include <algorithm>
#include <cmath>
// ---------------------------------------------------------------------------
// 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
// ---------------------------------------------------------------------------
/// <summary>
/// Process DMR data frame from the RF interface.
/// </summary>
/// <param name="data">Buffer containing data frame.</param>
/// <param name="len">Length of data frame.</param>
/// <returns></returns>
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;
}
/// <summary>
/// Process a data frame from the network.
/// </summary>
/// <param name="dmrData"></param>
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
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the ControlPacket class.
/// </summary>
/// <param name="slot">DMR slot.</param>
/// <param name="network">Instance of the BaseNetwork class.</param>
/// <param name="dumpCSBKData"></param>
/// <param name="debug">Flag indicating whether DMR debug is enabled.</param>
/// <param name="verbose">Flag indicating whether DMR verbose logging is enabled.</param>
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 */
}
/// <summary>
/// Finalizes a instance of the ControlPacket class.
/// </summary>
ControlPacket::~ControlPacket()
{
/* stub */
}

@ -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 <vector>
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:
/// <summary>Process a data frame from the RF interface.</summary>
bool process(uint8_t* data, uint32_t len);
/// <summary>Process a data frame from the network.</summary>
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;
/// <summary>Initializes a new instance of the ControlPacket class.</summary>
ControlPacket(Slot* slot, network::BaseNetwork* network, bool dumpCSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the DataPacket class.</summary>
~ControlPacket();
};
} // namespace dmr
#endif // __DMR_CONTROL_PACKET_H__

@ -98,160 +98,13 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
slotType.setColorCode(m_slot->m_colorCode); slotType.setColorCode(m_slot->m_colorCode);
slotType.setDataType(dataType); slotType.setDataType(dataType);
if (dataType == DT_VOICE_LC_HEADER) { if (dataType == DT_TERMINATOR_WITH_LC) {
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 (m_slot->m_rfState != RS_RF_AUDIO) if (m_slot->m_rfState != RS_RF_AUDIO)
return false; return false;
// Regenerate the LC data // Regenerate the LC data
lc::FullLC fullLC; 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 // Regenerate the Slot Type
slotType.encode(data + 2U); 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)); 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) { if (m_slot->m_rfTimeout) {
writeEndRF(); m_slot->writeEndRF();
return false; return false;
} }
else { else {
writeEndRF(); m_slot->writeEndRF();
return true; 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) { else if (dataType == DT_DATA_HEADER) {
if (m_slot->m_rfState == RS_RF_DATA) if (m_slot->m_rfState == RS_RF_DATA)
return true; return true;
@ -462,7 +181,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
m_slot->m_rfFrames = dataHeader.getBlocks(); m_slot->m_rfFrames = dataHeader.getBlocks();
m_slot->m_rfSeqNo = 0U; 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 // Regenerate the data header
dataHeader.encode(data + 2U); dataHeader.encode(data + 2U);
@ -499,7 +218,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
if (m_slot->m_rfFrames == 0U) { if (m_slot->m_rfFrames == 0U) {
::ActivityLog("DMR", true, "Slot %u ended RF data transmission", m_slot->m_slotNo); ::ActivityLog("DMR", true, "Slot %u ended RF data transmission", m_slot->m_slotNo);
writeEndRF(); m_slot->writeEndRF();
} }
return true; 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); 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) { if (m_debug) {
@ -590,190 +309,13 @@ void DataPacket::processNetwork(const data::Data& dmrData)
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
dmrData.getData(data + 2U); dmrData.getData(data + 2U);
if (dataType == DT_VOICE_LC_HEADER) { if (dataType == DT_TERMINATOR_WITH_LC) {
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 (m_slot->m_netState != RS_NET_AUDIO) if (m_slot->m_netState != RS_NET_AUDIO)
return; return;
// Regenerate the LC data // Regenerate the LC data
lc::FullLC fullLC; 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 // Regenerate the Slot Type
SlotType slotType; 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%%", ::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)); 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(); m_slot->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;
}
}
} }
else if (dataType == DT_DATA_HEADER) { else if (dataType == DT_DATA_HEADER) {
if (m_slot->m_netState == RS_NET_DATA) 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) { if (m_slot->m_netFrames == 0U) {
::ActivityLog("DMR", false, "Slot %u ended network data transmission", m_slot->m_slotNo); ::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) { 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) { if (m_slot->m_netState != RS_NET_DATA || m_slot->m_netFrames == 0U) {
writeEndNet(); m_slot->writeEndNet();
return; 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); LogMessage(LOG_NET, "DMR Slot %u, DT_RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo);
writeEndNet(); m_slot->writeEndNet();
} }
} }
else { else {
@ -1089,19 +498,15 @@ void DataPacket::processNetwork(const data::Data& dmrData)
/// <param name="network">Instance of the BaseNetwork class.</param> /// <param name="network">Instance of the BaseNetwork class.</param>
/// <param name="dumpDataPacket"></param> /// <param name="dumpDataPacket"></param>
/// <param name="repeatDataPacket"></param> /// <param name="repeatDataPacket"></param>
/// <param name="dumpCSBKData"></param>
/// <param name="debug">Flag indicating whether DMR debug is enabled.</param> /// <param name="debug">Flag indicating whether DMR debug is enabled.</param>
/// <param name="verbose">Flag indicating whether DMR verbose logging is enabled.</param> /// <param name="verbose">Flag indicating whether DMR verbose logging is enabled.</param>
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_slot(slot),
m_rfLC(NULL),
m_netLC(NULL),
m_pduUserData(NULL), m_pduUserData(NULL),
m_pduDataOffset(0U), m_pduDataOffset(0U),
m_lastRejectId(0U), m_lastRejectId(0U),
m_dumpDataPacket(dumpDataPacket), m_dumpDataPacket(dumpDataPacket),
m_repeatDataPacket(repeatDataPacket), m_repeatDataPacket(repeatDataPacket),
m_dumpCSBKData(dumpCSBKData),
m_verbose(verbose), m_verbose(verbose),
m_debug(debug) m_debug(debug)
{ {
@ -1116,113 +521,3 @@ DataPacket::~DataPacket()
{ {
delete[] m_pduUserData; delete[] m_pduUserData;
} }
/// <summary>
/// Helper to write RF end of frame data.
/// </summary>
/// <param name="writeEnd"></param>
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;
}
/// <summary>
/// Helper to write network end of frame data.
/// </summary>
/// <param name="writeEnd"></param>
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;
}

@ -53,6 +53,7 @@ namespace dmr
// Class Prototypes // Class Prototypes
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
class HOST_SW_API VoicePacket; class HOST_SW_API VoicePacket;
class HOST_SW_API ControlPacket;
class HOST_SW_API Slot; class HOST_SW_API Slot;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -69,12 +70,10 @@ namespace dmr
private: private:
friend class VoicePacket; friend class VoicePacket;
friend class ControlPacket;
friend class Slot; friend class Slot;
Slot* m_slot; Slot* m_slot;
lc::LC* m_rfLC;
lc::LC* m_netLC;
uint8_t* m_pduUserData; uint8_t* m_pduUserData;
uint32_t m_pduDataOffset; uint32_t m_pduDataOffset;
uint32_t m_lastRejectId; uint32_t m_lastRejectId;
@ -82,19 +81,13 @@ namespace dmr
bool m_dumpDataPacket; bool m_dumpDataPacket;
bool m_repeatDataPacket; bool m_repeatDataPacket;
bool m_dumpCSBKData;
bool m_verbose; bool m_verbose;
bool m_debug; bool m_debug;
/// <summary>Initializes a new instance of the DataPacket class.</summary> /// <summary>Initializes a new instance of the DataPacket class.</summary>
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);
/// <summary>Finalizes a instance of the DataPacket class.</summary> /// <summary>Finalizes a instance of the DataPacket class.</summary>
~DataPacket(); ~DataPacket();
/// <summary>Helper to write RF end of frame data.</summary>
void writeEndRF(bool writeEnd = false);
/// <summary>Helper to write network end of frame data.</summary>
void writeEndNet(bool writeEnd = false);
}; };
} // namespace dmr } // namespace dmr

@ -112,7 +112,9 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_rfLastDstId(0U), m_rfLastDstId(0U),
m_netState(RS_NET_IDLE), m_netState(RS_NET_IDLE),
m_netLastDstId(0U), m_netLastDstId(0U),
m_rfLC(NULL),
m_rfSeqNo(0U), m_rfSeqNo(0U),
m_netLC(NULL),
m_networkWatchdog(1000U, 0U, 1500U), m_networkWatchdog(1000U, 0U, 1500U),
m_rfTimeoutTimer(1000U, timeout), m_rfTimeoutTimer(1000U, timeout),
m_rfTGHang(1000U, tgHang), 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_interval.start();
m_voice = new VoicePacket(this, m_network, m_embeddedLCOnly, m_dumpTAData, debug, verbose); 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);
} }
/// <summary> /// <summary>
@ -152,6 +155,7 @@ Slot::~Slot()
{ {
delete m_voice; delete m_voice;
delete m_data; delete m_data;
delete m_control;
} }
/// <summary> /// <summary>
@ -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)); m_slotNo, m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rfTimeout) { if (m_rfTimeout) {
m_data->writeEndRF(); writeEndRF();
return false; return false;
} }
else { else {
m_data->writeEndRF(true); writeEndRF(true);
return true; return true;
} }
} }
if (data[0U] == TAG_LOST && m_rfState == RS_RF_DATA) { if (data[0U] == TAG_LOST && m_rfState == RS_RF_DATA) {
::ActivityLog("DMR", true, "Slot %u, RF data transmission lost", m_slotNo); ::ActivityLog("DMR", true, "Slot %u, RF data transmission lost", m_slotNo);
m_data->writeEndRF(); writeEndRF();
return false; return false;
} }
@ -255,8 +259,24 @@ bool Slot::processFrame(uint8_t *data, uint32_t len)
m_rfTGHang.start(); m_rfTGHang.start();
if (dataSync) { if (dataSync) {
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_data->process(data, len);
} }
}
return m_voice->process(data, len); return m_voice->process(data, len);
} }
@ -310,20 +330,23 @@ void Slot::processNetwork(const data::Data& dmrData)
switch (dataType) switch (dataType)
{ {
case DT_CSBK:
m_control->processNetwork(dmrData);
break;
case DT_VOICE_LC_HEADER: case DT_VOICE_LC_HEADER:
case DT_VOICE_PI_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_TERMINATOR_WITH_LC:
case DT_DATA_HEADER: case DT_DATA_HEADER:
case DT_CSBK:
case DT_RATE_12_DATA: case DT_RATE_12_DATA:
case DT_RATE_34_DATA: case DT_RATE_34_DATA:
case DT_RATE_1_DATA: case DT_RATE_1_DATA:
default:
m_data->processNetwork(dmrData); m_data->processNetwork(dmrData);
break; break;
case DT_VOICE_SYNC:
case DT_VOICE:
default:
m_voice->processNetwork(dmrData);
} }
} }
@ -380,11 +403,11 @@ void Slot::clock()
m_netFrames += 1U; m_netFrames += 1U;
::ActivityLog("DMR", false, "Slot %u network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", ::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_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 { else {
::ActivityLog("DMR", false, "Slot %u network watchdog has expired", m_slotNo); ::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) void Slot::writeNetworkRF(const uint8_t* data, uint8_t dataType, uint8_t errors)
{ {
assert(data != NULL); 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);
} }
/// <summary> /// <summary>
@ -820,6 +843,116 @@ void Slot::writeRF_TSCC_Bcast_Sys_Parm()
writeQueueRF(data); writeQueueRF(data);
} }
/// <summary>
/// Helper to write RF end of frame data.
/// </summary>
/// <param name="writeEnd"></param>
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;
}
/// <summary>
/// Helper to write network end of frame data.
/// </summary>
/// <param name="writeEnd"></param>
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;
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>

@ -34,6 +34,7 @@
#include "Defines.h" #include "Defines.h"
#include "dmr/Control.h" #include "dmr/Control.h"
#include "dmr/SiteData.h" #include "dmr/SiteData.h"
#include "dmr/ControlPacket.h"
#include "dmr/DataPacket.h" #include "dmr/DataPacket.h"
#include "dmr/VoicePacket.h" #include "dmr/VoicePacket.h"
#include "modem/Modem.h" #include "modem/Modem.h"
@ -56,6 +57,7 @@ namespace dmr
class HOST_SW_API Control; class HOST_SW_API Control;
class HOST_SW_API VoicePacket; class HOST_SW_API VoicePacket;
class HOST_SW_API DataPacket; class HOST_SW_API DataPacket;
class HOST_SW_API ControlPacket;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
@ -94,6 +96,8 @@ namespace dmr
VoicePacket* m_voice; VoicePacket* m_voice;
friend class DataPacket; friend class DataPacket;
DataPacket* m_data; DataPacket* m_data;
friend class ControlPacket;
ControlPacket* m_control;
uint32_t m_slotNo; uint32_t m_slotNo;
@ -104,8 +108,12 @@ namespace dmr
RPT_NET_STATE m_netState; RPT_NET_STATE m_netState;
uint32_t m_netLastDstId; uint32_t m_netLastDstId;
lc::LC* m_rfLC;
uint8_t m_rfSeqNo; uint8_t m_rfSeqNo;
lc::LC* m_netLC;
Timer m_networkWatchdog; Timer m_networkWatchdog;
Timer m_rfTimeoutTimer; Timer m_rfTimeoutTimer;
Timer m_rfTGHang; Timer m_rfTGHang;
@ -197,6 +205,11 @@ namespace dmr
/// <summary>Helper to write a TSCC Sys_Parm broadcast packet on the RF interface.</summary> /// <summary>Helper to write a TSCC Sys_Parm broadcast packet on the RF interface.</summary>
void writeRF_TSCC_Bcast_Sys_Parm(); void writeRF_TSCC_Bcast_Sys_Parm();
/// <summary>Helper to write RF end of frame data.</summary>
void writeEndRF(bool writeEnd = false);
/// <summary>Helper to write network end of frame data.</summary>
void writeEndNet(bool writeEnd = false);
/// <summary></summary> /// <summary></summary>
static void setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco = FLCO_GROUP, bool voice = true); static void setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco = FLCO_GROUP, bool voice = true);
/// <summary></summary> /// <summary></summary>

@ -56,6 +56,14 @@ using namespace dmr;
return false; \ 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 // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -69,8 +77,167 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
{ {
assert(data != NULL); assert(data != NULL);
bool dataSync = (data[1U] & DMR_SYNC_DATA) == DMR_SYNC_DATA;
bool voiceSync = (data[1U] & DMR_SYNC_VOICE) == DMR_SYNC_VOICE; 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 (voiceSync) {
if (m_slot->m_rfState == RS_RF_AUDIO) { if (m_slot->m_rfState == RS_RF_AUDIO) {
m_lastRfN = 0U; m_lastRfN = 0U;
@ -79,7 +246,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
Sync::addDMRAudioSync(data + 2U, m_slot->m_duplex); Sync::addDMRAudioSync(data + 2U, m_slot->m_duplex);
uint32_t errors = 0U; 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) { if (fid == FID_ETSI || fid == FID_DMRA) {
errors = m_fec.regenerateDMR(data + 2U); errors = m_fec.regenerateDMR(data + 2U);
if (m_verbose) { if (m_verbose) {
@ -94,7 +261,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
m_slot->m_rfFrames++; m_slot->m_rfFrames++;
m_slot->m_rfTGHang.start(); 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_rfEmbeddedReadN = (m_rfEmbeddedReadN + 1U) % 2U;
m_rfEmbeddedWriteN = (m_rfEmbeddedWriteN + 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; m_lastRfN = m_rfN;
uint32_t errors = 0U; 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) { if (fid == FID_ETSI || fid == FID_DMRA) {
errors = m_fec.regenerateDMR(data + 2U); errors = m_fec.regenerateDMR(data + 2U);
if (m_verbose) { if (m_verbose) {
@ -147,7 +314,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
m_slot->m_rfFrames++; m_slot->m_rfFrames++;
m_slot->m_rfTGHang.start(); 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 // Get the LCSS from the EMB
data::EMB emb; data::EMB emb;
@ -177,7 +344,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
} }
if (m_verbose) { if (m_verbose) {
logGPSPosition(m_slot->m_data->m_rfLC->getSrcId(), data); logGPSPosition(m_slot->m_rfLC->getSrcId(), data);
} }
break; 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 // The standby LC data
m_rfEmbeddedLC.setLC(*m_slot->m_data->m_rfLC); m_rfEmbeddedLC.setLC(*m_slot->m_rfLC);
m_rfEmbeddedData[0U].setLC(*m_slot->m_data->m_rfLC); m_rfEmbeddedData[0U].setLC(*m_slot->m_rfLC);
m_rfEmbeddedData[1U].setLC(*m_slot->m_data->m_rfLC); m_rfEmbeddedData[1U].setLC(*m_slot->m_rfLC);
// Create a dummy start frame to replace the received frame // Create a dummy start frame to replace the received frame
uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; 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); Sync::addDMRDataSync(start + 2U, m_slot->m_duplex);
lc::FullLC fullLC; 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 slotType;
slotType.setColorCode(m_slot->m_colorCode); 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 // Send the original audio frame out
uint32_t errors = 0U; 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) { if (fid == FID_ETSI || fid == FID_DMRA) {
errors = m_fec.regenerateDMR(data + 2U); errors = m_fec.regenerateDMR(data + 2U);
if (m_verbose) { if (m_verbose) {
@ -415,19 +582,196 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
dmrData.getData(data + 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) { if (m_slot->m_netState == RS_NET_IDLE) {
lc::LC* lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); lc::LC* lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId());
uint32_t dstId = lc->getDstId(); uint32_t dstId = lc->getDstId();
uint32_t srcId = lc->getSrcId(); uint32_t srcId = lc->getSrcId();
m_slot->m_data->m_netLC = lc; m_slot->m_netLC = lc;
// The standby LC data // The standby LC data
m_netEmbeddedLC.setLC(*m_slot->m_data->m_netLC); m_netEmbeddedLC.setLC(*m_slot->m_netLC);
m_netEmbeddedData[0U].setLC(*m_slot->m_data->m_netLC); m_netEmbeddedData[0U].setLC(*m_slot->m_netLC);
m_netEmbeddedData[1U].setLC(*m_slot->m_data->m_netLC); m_netEmbeddedData[1U].setLC(*m_slot->m_netLC);
m_lastFrameValid = false; m_lastFrameValid = false;
@ -448,7 +792,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
Sync::addDMRDataSync(start + 2U, m_slot->m_duplex); Sync::addDMRDataSync(start + 2U, m_slot->m_duplex);
lc::FullLC fullLC; 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 slotType;
slotType.setColorCode(m_slot->m_colorCode); 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_netState = RS_NET_AUDIO;
m_slot->m_netLastDstId = dstId; 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", ::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) { 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) { if (fid == FID_ETSI || fid == FID_DMRA) {
m_slot->m_netErrs += m_fec.regenerateDMR(data + 2U); m_slot->m_netErrs += m_fec.regenerateDMR(data + 2U);
if (m_verbose) { if (m_verbose) {
@ -531,7 +875,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
if (m_slot->m_netState != RS_NET_AUDIO) if (m_slot->m_netState != RS_NET_AUDIO)
return; 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) { if (fid == FID_ETSI || fid == FID_DMRA) {
m_slot->m_netErrs += m_fec.regenerateDMR(data + 2U); m_slot->m_netErrs += m_fec.regenerateDMR(data + 2U);
if (m_verbose) { if (m_verbose) {
@ -567,7 +911,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
Utils::dump(2U, text, data, 9U); Utils::dump(2U, text, data, 9U);
} }
logGPSPosition(m_slot->m_data->m_netLC->getSrcId(), data); logGPSPosition(m_slot->m_netLC->getSrcId(), data);
break; break;
case FLCO_TALKER_ALIAS_HEADER: case FLCO_TALKER_ALIAS_HEADER:
if (!(m_netTalkerId & TALKER_ID_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 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; data::EMB emb;
emb.setColorCode(m_slot->m_colorCode); emb.setColorCode(m_slot->m_colorCode);

Loading…
Cancel
Save

Powered by TurnKey Linux.