refactor DMR PDU processing to be more in-line with how P25 PDU processing is done; implement support on the FNE to inspect and dispatch DMR PDUs properly;

pull/61/head
Bryan Biedenkapp 2 years ago
parent b343473a87
commit dc29208e70

@ -110,9 +110,11 @@ namespace dmr
const uint32_t MAX_PDU_COUNT = 32U;
const uint32_t PDU_UNCONFIRMED_LENGTH_BYTES = 12U;
const uint32_t PDU_CONFIRMED_LENGTH_BYTES = 18U;
const uint32_t PDU_UNCODED_LENGTH_BYTES = 24U;
const uint32_t DMR_PDU_UNCONFIRMED_LENGTH_BYTES = 12U;
const uint32_t DMR_PDU_CONFIRMED_LENGTH_BYTES = 18U;
const uint32_t DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES = 16U;
const uint32_t DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES = 10U;
const uint32_t DMR_PDU_UNCODED_LENGTH_BYTES = 24U;
const uint32_t MI_LENGTH_BYTES = 4U; // This was guessed based on OTA data captures -- the message indicator seems to be the same length as a source/destination address
/** @} */

@ -0,0 +1,369 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "dmr/DMRDefines.h"
#include "dmr/data/DataBlock.h"
#include "edac/CRC.h"
#include "Log.h"
#include "Utils.h"
using namespace dmr;
using namespace dmr::defines;
using namespace dmr::data;
#include <cassert>
#include <cstring>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a new instance of the DataBlock class. */
DataBlock::DataBlock() :
m_serialNo(0U),
m_lastBlock(false),
m_bptc(),
m_trellis(),
m_dataType(DataType::RATE_34_DATA),
m_DPF(DPF::CONFIRMED_DATA),
m_data(nullptr)
{
m_data = new uint8_t[DMR_PDU_UNCODED_LENGTH_BYTES];
}
/* Finalizes a instance of the DataBlock class. */
DataBlock::~DataBlock()
{
delete[] m_data;
}
/* Decodes DMR PDU data block. */
bool DataBlock::decode(const uint8_t* data, const DataHeader& header)
{
assert(data != nullptr);
assert(m_data != nullptr);
uint8_t buffer[DMR_PDU_UNCODED_LENGTH_BYTES];
::memset(buffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES);
m_DPF = header.getDPF();
// set these to reasonable defaults
m_serialNo = 0U;
m_lastBlock = false;
if (m_DPF == DPF::CONFIRMED_DATA) {
try {
if (m_dataType == DataType::RATE_34_DATA) {
bool valid = m_trellis.decode34(data, buffer, true);
if (!valid) {
LogError(LOG_DMR, "DataBlock::decode(), failed to decode Trellis 3/4 rate coding");
return false;
}
}
else if (m_dataType == DataType::RATE_12_DATA) {
m_bptc.decode(data, buffer);
}
else {
LogError(LOG_DMR, "DataBlock::decode(), cowardly refusing to decode confirmed full-rate (rate 1) data");
return false;
}
#if DEBUG_DMR_PDU_DATA
Utils::dump(1U, "DMR, DataBlock::decode(), Confirmed PDU Data Block", buffer, DMR_PDU_UNCODED_LENGTH_BYTES);
#endif
m_serialNo = (buffer[0] & 0xFEU) >> 1; // Confirmed Data Serial No.
uint16_t crc = ((buffer[0] & 0x01U) << 8) + buffer[1]; // CRC-9 Check Sum
::memset(m_data, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES);
if (m_dataType == DataType::RATE_34_DATA)
::memcpy(m_data, buffer + 2U, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data
else if (m_dataType == DataType::RATE_12_DATA)
::memcpy(m_data, buffer + 2U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); // Payload Data
else {
LogError(LOG_DMR, "DataBlock::decode(), failed to decode block, invalid dataType = $%02X", m_dataType);
return false;
}
uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES];
::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES);
// generate CRC buffer
uint32_t bufferLen = DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES;
if (m_dataType == DataType::RATE_12_DATA)
bufferLen = DMR_PDU_UNCONFIRMED_LENGTH_BYTES;
uint32_t crcBitLength = 144U;
if (m_dataType == DataType::RATE_12_DATA)
crcBitLength = 96U;
for (uint32_t i = 16U; i < crcBitLength; i++) {
bool b = READ_BIT(buffer, i);
WRITE_BIT(crcBuffer, i - 16U, b);
}
for (uint32_t i = 0U; i < 6U; i++) {
bool b = READ_BIT(buffer, i);
WRITE_BIT(crcBuffer, i + (crcBitLength - 16U), b);
}
// compute CRC-9 for the packet
uint16_t calculated = edac::CRC::createCRC9(crcBuffer, crcBitLength - 9U);
calculated = ~calculated & 0x1FFU;
if ((crc ^ calculated) != 0) {
LogWarning(LOG_DMR, "DMR, dataType = $%02X, invalid crc = $%04X != $%04X (computed)", m_dataType, crc, calculated);
}
#if DEBUG_DMR_PDU_DATA
LogDebug(LOG_DMR, "DMR, dataType = $%02X, crc = $%04X, calculated = $%04X", m_dataType, crc, calculated);
#endif
}
catch (...) {
Utils::dump(2U, "DMR, decoding excepted with input data", data, DMR_PDU_UNCODED_LENGTH_BYTES);
return false;
}
}
else if (m_DPF == DPF::UNCONFIRMED_DATA || m_DPF == DPF::RESPONSE || m_DPF == DPF::DEFINED_RAW ||
m_DPF == DPF::DEFINED_SHORT || m_DPF == DPF::UDT) {
try {
if (m_dataType == DataType::RATE_34_DATA) {
bool valid = m_trellis.decode34(data, buffer, true);
if (!valid) {
LogError(LOG_DMR, "DataBlock::decode(), failed to decode Trellis 3/4 rate coding");
return false;
}
}
else if (m_dataType == DataType::RATE_12_DATA) {
m_bptc.decode(data, buffer);
}
else {
::memcpy(buffer, data, DMR_PDU_UNCODED_LENGTH_BYTES);
return true; // never do any further processing for uncoded
}
::memset(m_data, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES);
if (m_dataType == DataType::RATE_34_DATA)
::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data
else if (m_dataType == DataType::RATE_12_DATA)
::memcpy(m_data, buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); // Payload Data
else {
LogError(LOG_DMR, "DataBlock::decode(), failed to decode block, invalid dataType = $%02X", m_dataType);
return false;
}
}
catch (...) {
Utils::dump(2U, "DMR, decoding excepted with input data", data, DMR_PDU_UNCODED_LENGTH_BYTES);
return false;
}
}
else {
LogError(LOG_P25, "unknown DPF value in PDU, dpf = $%02X", m_DPF);
}
return true;
}
/* Encodes a DMR PDU data block. */
void DataBlock::encode(uint8_t* data)
{
assert(data != nullptr);
assert(m_data != nullptr);
if (m_DPF == DPF::CONFIRMED_DATA) {
if (m_dataType == DataType::RATE_34_DATA) {
uint8_t buffer[DMR_PDU_CONFIRMED_LENGTH_BYTES];
::memset(buffer, 0x00U, DMR_PDU_CONFIRMED_LENGTH_BYTES);
buffer[0U] = ((m_serialNo << 1) & 0xFEU); // Confirmed Data Serial No.
::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data
uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES];
::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES);
// generate CRC buffer
uint32_t bufferLen = DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES;
uint32_t crcBitLength = 144U;
for (uint32_t i = 2U; i < bufferLen; i++)
crcBuffer[i - 2U] = buffer[2U];
for (uint32_t i = 0; i < 6U; i++) {
bool b = READ_BIT(buffer, i);
WRITE_BIT(crcBuffer, i + (crcBitLength - 15U), b);
}
uint16_t crc = edac::CRC::createCRC9(crcBuffer, 135U);
buffer[0U] = buffer[0U] + ((crc >> 8) & 0x01U); // CRC-9 Check Sum (b8)
buffer[1U] = (crc & 0xFFU); // CRC-9 Check Sum (b0 - b7)
#if DEBUG_DMR_PDU_DATA
Utils::dump(1U, "DMR, DataBlock::encode(), Confirmed PDU Data Block", buffer, DMR_PDU_CONFIRMED_LENGTH_BYTES);
#endif
m_trellis.encode34(buffer, data, true);
}
else if (m_dataType == DataType::RATE_12_DATA) {
uint8_t buffer[DMR_PDU_UNCONFIRMED_LENGTH_BYTES];
::memset(buffer, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
buffer[0U] = ((m_serialNo << 1) & 0xFEU); // Confirmed Data Serial No.
::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES); // Payload Data
uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES];
::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES);
// generate CRC buffer
uint32_t bufferLen = DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES;
uint32_t crcBitLength = 96U;
for (uint32_t i = 2U; i < bufferLen; i++)
crcBuffer[i - 2U] = buffer[2U];
for (uint32_t i = 0; i < 6U; i++) {
bool b = READ_BIT(buffer, i);
WRITE_BIT(crcBuffer, i + (crcBitLength - 15U), b);
}
uint16_t crc = edac::CRC::createCRC9(crcBuffer, 87U);
buffer[0U] = buffer[0U] + ((crc >> 8) & 0x01U); // CRC-9 Check Sum (b8)
buffer[1U] = (crc & 0xFFU); // CRC-9 Check Sum (b0 - b7)
::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
#if DEBUG_DMR_PDU_DATA
Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
#endif
m_bptc.encode(buffer, data);
}
else {
LogError(LOG_DMR, "DataBlock::encode(), cowardly refusing to encode confirmed full-rate (rate 1) data");
return;
}
}
else if (m_DPF == DPF::UNCONFIRMED_DATA || m_DPF == DPF::RESPONSE || m_DPF == DPF::DEFINED_RAW ||
m_DPF == DPF::DEFINED_SHORT || m_DPF == DPF::UDT) {
if (m_dataType == DataType::RATE_34_DATA) {
uint8_t buffer[DMR_PDU_CONFIRMED_LENGTH_BYTES];
::memset(buffer, 0x00U, DMR_PDU_CONFIRMED_LENGTH_BYTES);
::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_LENGTH_BYTES);
#if DEBUG_DMR_PDU_DATA
Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_CONFIRMED_LENGTH_BYTES);
#endif
m_trellis.encode34(buffer, data, true);
}
else if (m_dataType == DataType::RATE_12_DATA) {
uint8_t buffer[DMR_PDU_UNCONFIRMED_LENGTH_BYTES];
::memset(buffer, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
#if DEBUG_DMR_PDU_DATA
Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
#endif
m_bptc.encode(buffer, data);
}
else {
uint8_t buffer[DMR_PDU_UNCODED_LENGTH_BYTES];
::memset(buffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES);
::memcpy(buffer, m_data, DMR_PDU_UNCODED_LENGTH_BYTES);
::memcpy(data, buffer, DMR_PDU_UNCODED_LENGTH_BYTES);
}
}
else {
LogError(LOG_P25, "unknown DPF value in PDU, dpf = $%02X", m_DPF);
return;
}
}
/* Sets the data type. */
void DataBlock::setDataType(const DataType::E& dataType)
{
m_dataType = dataType;
}
/* Gets the data type. */
DataType::E DataBlock::getDataType() const
{
return m_dataType;
}
/* Sets the data format. */
void DataBlock::setFormat(const DPF::E fmt)
{
m_DPF = fmt;
}
/* Sets the data format from the data header. */
void DataBlock::setFormat(const DataHeader& header)
{
m_DPF = header.getDPF();
}
/* Gets the data format. */
DPF::E DataBlock::getFormat() const
{
return m_DPF;
}
/* Sets the raw data stored in the data block. */
void DataBlock::setData(const uint8_t* buffer)
{
assert(buffer != nullptr);
assert(m_data != nullptr);
if (m_dataType == DataType::RATE_34_DATA)
::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES);
else if (m_dataType == DataType::RATE_12_DATA)
::memcpy(m_data, buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
else if (m_dataType == DataType::RATE_1_DATA)
::memcpy(m_data, buffer, DMR_PDU_UNCODED_LENGTH_BYTES);
else
LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType);
}
/* Gets the raw data stored in the data block. */
uint32_t DataBlock::getData(uint8_t* buffer) const
{
assert(buffer != nullptr);
assert(m_data != nullptr);
if (m_dataType == DataType::RATE_34_DATA) {
::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES);
return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES;
} else if (m_dataType == DataType::RATE_12_DATA) {
::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
return DMR_PDU_UNCONFIRMED_LENGTH_BYTES;
} else if (m_dataType == DataType::RATE_1_DATA) {
::memcpy(buffer, m_data, DMR_PDU_UNCODED_LENGTH_BYTES);
return DMR_PDU_UNCODED_LENGTH_BYTES;
} else {
LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType);
return 0U;
}
}

@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file DataBlock.h
* @ingroup dmr_pdu
* @file DataBlock.cpp
* @ingroup dmr_pdu
*/
#if !defined(__DMR_DATA__DATA_BLOCK_H__)
#define __DMR_DATA__DATA_BLOCK_H__
#include "common/Defines.h"
#include "common/dmr/DMRDefines.h"
#include "common/dmr/data/DataHeader.h"
#include "common/edac/BPTC19696.h"
#include "common/edac/Trellis.h"
#include <string>
namespace dmr
{
namespace data
{
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Represents a data block for PDU DMR packets.
* @ingroup dmr_pdu
*/
class HOST_SW_API DataBlock {
public:
/**
* @brief Initializes a new instance of the DataBlock class.
*/
DataBlock();
/**
* @brief Finalizes a instance of the DataBlock class.
*/
~DataBlock();
/**
* @brief Decodes DMR PDU data block.
* @param[in] data Buffer containing a PDU data block to decode.
* @param header DMR PDU data header.
* @returns bool True, if PDU data block decoded, otherwise false.
*/
bool decode(const uint8_t* data, const DataHeader& header);
/**
* @brief Encodes a DMR PDU data block.
* @param[out] data Buffer to encode a PDU data block.
*/
void encode(uint8_t* data);
/**
* @brief Sets the data format from the data header.
* @param dataType DMR data type.
*/
void setDataType(const defines::DataType::E& dataType);
/**
* @brief Gets the data type.
* @returns defines::DataType::E DMR data type.
*/
defines::DataType::E getDataType() const;
/**
* @brief Sets the data format.
* @param fmt PDU data foramt.
*/
void setFormat(const defines::DPF::E fmt);
/**
* @brief Sets the data format from the data header.
* @param header P25 PDU data header.
*/
void setFormat(const DataHeader& header);
/**
* @brief Gets the data format.
* @returns defines::DPF::E PDU data format.
*/
defines::DPF::E getFormat() const;
/**
* @brief Sets the raw data stored in the data block.
* @param[in] buffer Buffer containing bytes to store in data block.
*/
void setData(const uint8_t* buffer);
/**
* @brief Gets the raw data stored in the data block.
* @param[out] buffer Buffer to copy bytes in data block to.
* @returns uint32_t Number of bytes copied.
*/
uint32_t getData(uint8_t* buffer) const;
public:
/**
* @brief Sets the data block serial number.
*/
__PROPERTY(uint8_t, serialNo, SerialNo);
/**
* @brief Flag indicating this is the last block in a sequence of block.
*/
__PROPERTY(bool, lastBlock, LastBlock);
private:
edac::BPTC19696 m_bptc;
edac::Trellis m_trellis;
defines::DataType::E m_dataType;
defines::DPF::E m_DPF;
uint8_t* m_data;
};
} // namespace data
} // namespace dmr
#endif // __P25_DATA__DATA_BLOCK_H__

@ -12,7 +12,6 @@
#include "Defines.h"
#include "dmr/DMRDefines.h"
#include "dmr/data/DataHeader.h"
#include "edac/BPTC19696.h"
#include "edac/CRC.h"
#include "Utils.h"
@ -22,6 +21,7 @@ using namespace dmr::data;
#include <cassert>
#include <cstring>
#include <cmath>
// ---------------------------------------------------------------------------
// Constants
@ -42,24 +42,26 @@ DataHeader::DataHeader() :
m_sap(0U),
m_fsn(0U),
m_Ns(0U),
m_padCount(0U),
m_blocksToFollow(0U),
m_padLength(0U),
m_F(false),
m_S(false),
m_dataFormat(0U),
m_srcId(0U),
m_dstId(0U),
m_blocks(0U),
m_rspClass(PDUResponseClass::NACK),
m_rspType(PDUResponseType::NACK_ILLEGAL),
m_rspStatus(0U),
m_srcPort(0U),
m_dstPort(0U),
m_bptc(),
m_data(nullptr),
m_SF(false),
m_PF(false),
m_UDTO(0U)
{
m_data = new uint8_t[DMR_LC_HEADER_LENGTH_BYTES];
::memset(m_data, 0x00U, DMR_LC_HEADER_LENGTH_BYTES);
}
/* Finalizes a instance of the DataHeader class. */
@ -79,13 +81,13 @@ DataHeader& DataHeader::operator=(const DataHeader& header)
m_sap = header.m_sap;
m_fsn = header.m_fsn;
m_Ns = header.m_Ns;
m_padCount = header.m_padCount;
m_blocksToFollow = header.m_blocksToFollow;
m_padLength = header.m_padLength;
m_F = header.m_F;
m_S = header.m_S;
m_dataFormat = header.m_dataFormat;
m_srcId = header.m_srcId;
m_dstId = header.m_dstId;
m_blocks = header.m_blocks;
m_rspClass = header.m_rspClass;
m_rspType = header.m_rspType;
m_rspStatus = header.m_rspStatus;
@ -109,8 +111,7 @@ bool DataHeader::decode(const uint8_t* data)
assert(data != nullptr);
// decode BPTC (196,96) FEC
edac::BPTC19696 bptc;
bptc.decode(data, m_data);
m_bptc.decode(data, m_data);
// make sure the CRC-CCITT 16 was actually included (the network tends to zero the CRC)
if (m_data[10U] != 0x00U && m_data[11U] != 0x00U) {
@ -144,8 +145,8 @@ bool DataHeader::decode(const uint8_t* data)
#endif
m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point
m_dataFormat = (m_data[1U] & 0x0FU); // UDT Format
m_blocks = (m_data[8U] & 0x03U) + 1U; // Blocks To Follow
m_padCount = (m_data[8U] & 0xF8U) >> 3; // Pad Nibble
m_blocksToFollow = (m_data[8U] & 0x03U) + 1U; // Blocks To Follow
m_padLength = (m_data[8U] & 0xF8U) >> 3; // Pad Nibble
m_SF = (m_data[9U] & 0x80U) == 0x80U; // Supplemental Flag
m_PF = (m_data[9U] & 0x40U) == 0x40U; // Protect Flag
m_UDTO = m_data[9U] & 0x3FU; // UDT Opcode
@ -156,9 +157,9 @@ bool DataHeader::decode(const uint8_t* data)
Utils::dump(1U, "DMR, DataHeader::decode(), Unconfirmed Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES);
#endif
m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point
m_padCount = (m_data[0U] & 0x10U) + (m_data[1U] & 0x0FU); // Octet Pad Count
m_padLength = (m_data[0U] & 0x10U) + (m_data[1U] & 0x0FU); // Octet Pad Count
m_F = (m_data[8U] & 0x80U) == 0x80U; // Full Message Flag
m_blocks = m_data[8U] & 0x7FU; // Blocks To Follow
m_blocksToFollow = m_data[8U] & 0x7FU; // Blocks To Follow
m_fsn = m_data[9U] & 0x0FU; // Fragment Sequence Number
break;
@ -167,9 +168,9 @@ bool DataHeader::decode(const uint8_t* data)
Utils::dump(1U, "DMR, DataHeader::decode(), Confirmed Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES);
#endif
m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point
m_padCount = (m_data[0U] & 0x10U) + (m_data[1U] & 0x0FU); // Octet Pad Count
m_padLength = (m_data[0U] & 0x10U) + (m_data[1U] & 0x0FU); // Octet Pad Count
m_F = (m_data[8U] & 0x80U) == 0x80U; // Full Message Flag
m_blocks = m_data[8U] & 0x7FU; // Blocks To Follow
m_blocksToFollow = m_data[8U] & 0x7FU; // Blocks To Follow
m_S = (m_data[9U] & 0x80U) == 0x80U; // Synchronize Flag
m_Ns = (m_data[9U] >> 4) & 0x07U; // Send Sequence Number
m_fsn = m_data[9U] & 0x0FU; // Fragement Sequence Number
@ -180,7 +181,7 @@ bool DataHeader::decode(const uint8_t* data)
Utils::dump(1U, "DMR, DataHeader::decode(), Response Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES);
#endif
m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point
m_blocks = m_data[8U] & 0x7FU; // Blocks To Follow
m_blocksToFollow = m_data[8U] & 0x7FU; // Blocks To Follow
m_rspClass = (m_data[9U] >> 6) & 0x03U; // Response Class
m_rspType = (m_data[9U] >> 3) & 0x07U; // Response Type
m_rspStatus = m_data[9U] & 0x07U; // Response Status
@ -191,11 +192,11 @@ bool DataHeader::decode(const uint8_t* data)
Utils::dump(1U, "DMR, DataHeader::decode(), Defined Short Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES);
#endif
m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point
m_blocks = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU); // Blocks To Follow
m_blocksToFollow = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU); // Blocks To Follow
m_F = (m_data[8U] & 0x01U) == 0x01U; // Full Message Flag
m_S = (m_data[8U] & 0x02U) == 0x02U; // Synchronize Flag
m_dataFormat = (m_data[8U] & 0xFCU) >> 2; // Defined Data Format
m_padCount = m_data[9U]; // Bit Padding
m_padLength = m_data[9U]; // Bit Padding
break;
case DPF::DEFINED_RAW:
@ -203,7 +204,7 @@ bool DataHeader::decode(const uint8_t* data)
Utils::dump(1U, "DMR, DataHeader::decode(), Raw Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES);
#endif
m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point
m_blocks = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU); // Blocks To Follow
m_blocksToFollow = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU); // Blocks To Follow
m_F = (m_data[8U] & 0x01U) == 0x01U; // Full Message Flag
m_S = (m_data[8U] & 0x02U) == 0x02U; // Synchronize Flag
m_dstPort = (m_data[8U] & 0x1CU) >> 2; // Destination Port
@ -220,7 +221,7 @@ bool DataHeader::decode(const uint8_t* data)
/* Encodes a DMR data header. */
void DataHeader::encode(uint8_t* data) const
void DataHeader::encode(uint8_t* data)
{
assert(data != nullptr);
@ -262,8 +263,8 @@ void DataHeader::encode(uint8_t* data) const
case DPF::UDT:
m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point
(m_dataFormat & 0x0FU); // UDT Format
m_data[8U] = ((m_padCount & 0x1FU) << 3) + // Pad Nibble
(m_blocks - 1U); // Blocks To Follow
m_data[8U] = ((m_padLength & 0x1FU) << 3) + // Pad Nibble
(m_blocksToFollow - 1U); // Blocks To Follow
m_data[9U] = (m_SF ? 0x80U : 0x00U) + // Supplemental Flag
(m_PF ? 0x40U : 0x00U) + // Protect Flag
(m_UDTO & 0x3F); // UDT Opcode
@ -273,11 +274,11 @@ void DataHeader::encode(uint8_t* data) const
break;
case DPF::UNCONFIRMED_DATA:
m_data[0U] = m_data[0U] + (m_padCount & 0x10U); // Octet Pad Count MSB
m_data[0U] = m_data[0U] + (m_padLength & 0x10U); // Octet Pad Count MSB
m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point
(m_padCount & 0x0FU); // Octet Pad Count LSB
(m_padLength & 0x0FU); // Octet Pad Count LSB
m_data[8U] = (m_F ? 0x80U : 0x00U) + // Full Message Flag
(m_blocks & 0x7FU); // Blocks To Follow
(m_blocksToFollow & 0x7FU); // Blocks To Follow
m_data[9U] = m_fsn; // Fragment Sequence Number
#if DEBUG_DMR_PDU_DATA
Utils::dump(1U, "DMR, DataHeader::decode(), Unconfirmed Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES);
@ -285,11 +286,11 @@ void DataHeader::encode(uint8_t* data) const
break;
case DPF::CONFIRMED_DATA:
m_data[0U] = m_data[0U] + (m_padCount & 0x10U); // Octet Pad Count MSB
m_data[0U] = m_data[0U] + (m_padLength & 0x10U); // Octet Pad Count MSB
m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point
(m_padCount & 0x0FU); // Octet Pad Count LSB
(m_padLength & 0x0FU); // Octet Pad Count LSB
m_data[8U] = (m_F ? 0x80U : 0x00U) + // Full Message Flag
(m_blocks & 0x7FU); // Blocks To Follow
(m_blocksToFollow & 0x7FU); // Blocks To Follow
m_data[9U] = (m_S ? 0x80U : 0x00U) + // Synchronize Flag
((m_Ns & 0x07U) << 4) + // Send Sequence Number
(m_fsn & 0x0FU); // Fragment Sequence Number
@ -300,7 +301,7 @@ void DataHeader::encode(uint8_t* data) const
case DPF::RESPONSE:
m_data[1U] = ((m_sap & 0x0FU) << 4); // Service Access Point
m_data[8U] = m_blocks & 0x7FU; // Blocks To Follow
m_data[8U] = m_blocksToFollow & 0x7FU; // Blocks To Follow
m_data[9U] = ((m_rspClass & 0x03U) << 6) + // Response Class
((m_rspType & 0x07U) << 3) + // Response Type
((m_rspStatus & 0x07U)); // Response Status
@ -310,22 +311,22 @@ void DataHeader::encode(uint8_t* data) const
break;
case DPF::DEFINED_SHORT:
m_data[0U] = m_data[0U] + (m_blocks & 0x30U); // Blocks To Follow MSB
m_data[0U] = m_data[0U] + (m_blocksToFollow & 0x30U); // Blocks To Follow MSB
m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point
(m_blocks & 0x0FU); // Blocks To Follow LSB
(m_blocksToFollow & 0x0FU); // Blocks To Follow LSB
m_data[8U] = (m_F ? 0x01U : 0x00U) + // Full Message Flag
(m_S ? 0x02U : 0x00U) + // Synchronize Flag
((m_dataFormat & 0xFCU) << 2); // Defined Data Format
m_data[9U] = m_padCount; // Bit Padding
m_data[9U] = m_padLength; // Bit Padding
#if DEBUG_DMR_PDU_DATA
Utils::dump(1U, "DMR, DataHeader::decode(), Defined Short Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES);
#endif
break;
case DPF::DEFINED_RAW:
m_data[0U] = m_data[0U] + (m_blocks & 0x30U); // Blocks To Follow MSB
m_data[0U] = m_data[0U] + (m_blocksToFollow & 0x30U); // Blocks To Follow MSB
m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point
(m_blocks & 0x0FU); // Blocks To Follow LSB
(m_blocksToFollow & 0x0FU); // Blocks To Follow LSB
m_data[8U] = (m_F ? 0x01U : 0x00U) + // Full Message Flag
(m_S ? 0x02U : 0x00U) + // Synchronize Flag
((m_dstPort & 0x07U) << 2) + // Destination Port
@ -362,6 +363,97 @@ void DataHeader::encode(uint8_t* data) const
}
// encode BPTC (196,96) FEC
edac::BPTC19696 bptc;
bptc.encode(m_data, data);
m_bptc.encode(m_data, data);
}
/* Helper to reset data values to defaults. */
void DataHeader::reset()
{
m_GI = false;
m_A = false;
m_DPF = DPF::UDT;
m_sap = 0U;
m_fsn = 0U;
m_Ns = 0U;
m_blocksToFollow = 0U;
m_padLength = 0U;
m_F = false;
m_S = false;
m_dataFormat = 0U;
m_srcId = 0U;
m_dstId = 0U;
m_rspClass = PDUResponseClass::NACK;
m_rspType = PDUResponseType::NACK_ILLEGAL;
m_rspStatus = 0U;
m_srcPort = 0U;
m_dstPort = 0U;
::memset(m_data, 0x00U, DMR_LC_HEADER_LENGTH_BYTES);
m_SF = false;
m_PF = false;
m_UDTO = 0U;
}
/* Gets the total length in bytes of enclosed packet data. */
uint32_t DataHeader::getPacketLength() const
{
if (m_DPF == DPF::RESPONSE) {
return 0U; // responses have no packet length as they are header only
}
if (m_DPF == DPF::CONFIRMED_DATA) {
return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength;
}
else {
return DMR_PDU_UNCONFIRMED_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength;
}
}
/* Gets the raw header data. */
uint32_t DataHeader::getData(uint8_t* buffer) const
{
assert(buffer != nullptr);
assert(m_data != nullptr);
::memcpy(buffer, m_data, DMR_LC_HEADER_LENGTH_BYTES);
return DMR_LC_HEADER_LENGTH_BYTES;
}
/* Helper to calculate the number of blocks to follow and padding length for a PDU. */
void DataHeader::calculateLength(uint32_t packetLength)
{
uint32_t len = packetLength + 4U; // packet length + CRC32
uint32_t blockLen = (m_DPF == DPF::CONFIRMED_DATA) ? DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES : DMR_PDU_UNCONFIRMED_LENGTH_BYTES;
if (len > blockLen) {
m_padLength = blockLen - (len % blockLen);
m_blocksToFollow = (uint8_t)ceilf((float)len / (float)blockLen);
} else {
m_padLength = 0U;
m_blocksToFollow = 1U;
}
}
/* Helper to determine the pad length for a given packet length. */
uint32_t DataHeader::calculatePadLength(DPF::E dpf, uint32_t packetLength)
{
uint32_t len = packetLength + 4U; // packet length + CRC32
if (dpf == DPF::CONFIRMED_DATA) {
return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES - (len % DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES);
}
else {
return DMR_PDU_UNCONFIRMED_LENGTH_BYTES - (len % DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
}
}

@ -5,19 +5,24 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL
* Copyright (C) 2021,2024 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file DataHeader.h
* @defgroup dmr_pdu Packet Data Unit
* @brief Implementation for the data handling of ETSI TS-102 packet data.
* @ingroup dmr
*
* @file DataHeader.h
* @ingroup dmr_pdu
* @file DataHeader.cpp
* @ingroup dmr
* @ingroup dmr_pdu
*/
#if !defined(__DMR_DATA__DATA_HEADER_H__)
#define __DMR_DATA__DATA_HEADER_H__
#include "common/Defines.h"
#include "common/edac/BPTC19696.h"
namespace dmr
{
@ -28,8 +33,8 @@ namespace dmr
// ---------------------------------------------------------------------------
/**
* @brief Represents a DMR data header.
* @ingroup dmr
* @brief Represents the data header for PDU DMR packets.
* @ingroup dmr_pdu
*/
class HOST_SW_API DataHeader {
public:
@ -58,7 +63,39 @@ namespace dmr
* @brief Encodes a DMR data header.
* @param[out] data Buffer to encode a DMR data header.
*/
void encode(uint8_t* data) const;
void encode(uint8_t* data);
/**
* @brief Helper to reset data values to defaults.
*/
void reset();
/**
* @brief Gets the total length in bytes of enclosed packet data.
* @returns uint32_t Total length of packet in bytes.
*/
uint32_t getPacketLength() const;
/**
* @brief Gets the raw header data.
* @param[out] buffer Buffer to copy raw header data to.
* @returns uint32_t Length of data copied.
*/
uint32_t getData(uint8_t* buffer) const;
/**
* @brief Helper to calculate the number of blocks to follow and padding length for a PDU.
* @param packetLength Length of PDU.
*/
void calculateLength(uint32_t packetLength);
/**
* @brief Helper to determine the pad length for a given packet length.
* @param dpf PDU format type.
* @param packetLength Length of PDU.
* @returns uint32_t Number of pad bytes.
*/
static uint32_t calculatePadLength(defines::DPF::E dpf, uint32_t packetLength);
public:
/**
@ -88,11 +125,14 @@ namespace dmr
*/
__PROPERTY(uint8_t, Ns, Ns);
/**
* @brief Gets the number of data blocks following the header.
*/
__PROPERTY(uint32_t, blocksToFollow, BlocksToFollow);
/**
* @brief Count of block padding.
*/
__PROPERTY(uint8_t, padCount, PadCount);
__PROPERTY(uint8_t, padLength, PadLength);
/**
* @brief Full Message Flag.
*/
@ -116,11 +156,6 @@ namespace dmr
*/
__PROPERTY(uint32_t, dstId, DstId);
/**
* @brief Gets the number of data blocks following the header.
*/
__PROPERTY(uint32_t, blocks, Blocks);
/**
* @brief Response class.
*/
@ -144,6 +179,8 @@ namespace dmr
__PROPERTY(uint8_t, dstPort, DstPort);
private:
edac::BPTC19696 m_bptc;
uint8_t* m_data;
bool m_SF;
bool m_PF;

@ -10,7 +10,7 @@
*/
#include "Defines.h"
#include "dmr/DMRDefines.h"
#include "dmr/data/Data.h"
#include "dmr/data/NetData.h"
using namespace dmr;
using namespace dmr::defines;
@ -23,9 +23,9 @@ using namespace dmr::data;
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a new instance of the Data class. */
/* Initializes a new instance of the NetData class. */
Data::Data(const Data& data) :
NetData::NetData(const NetData& data) :
m_slotNo(data.m_slotNo),
m_srcId(data.m_srcId),
m_dstId(data.m_dstId),
@ -41,9 +41,9 @@ Data::Data(const Data& data) :
::memcpy(m_data, data.m_data, 2U * DMR_FRAME_LENGTH_BYTES);
}
/* Initializes a new instance of the Data class. */
/* Initializes a new instance of the NetData class. */
Data::Data() :
NetData::NetData() :
m_slotNo(1U),
m_srcId(0U),
m_dstId(0U),
@ -58,16 +58,16 @@ Data::Data() :
m_data = new uint8_t[2U * DMR_FRAME_LENGTH_BYTES];
}
/* Finalizes a instance of the Data class. */
/* Finalizes a instance of the NetData class. */
Data::~Data()
NetData::~NetData()
{
delete[] m_data;
}
/* Equals operator. */
Data& Data::operator=(const Data& data)
NetData& NetData::operator=(const NetData& data)
{
if (this != &data) {
::memcpy(m_data, data.m_data, DMR_FRAME_LENGTH_BYTES);
@ -88,7 +88,7 @@ Data& Data::operator=(const Data& data)
/* Sets raw data. */
void Data::setData(const uint8_t* buffer)
void NetData::setData(const uint8_t* buffer)
{
assert(buffer != nullptr);
@ -97,7 +97,7 @@ void Data::setData(const uint8_t* buffer)
/* Gets raw data. */
uint32_t Data::getData(uint8_t* buffer) const
uint32_t NetData::getData(uint8_t* buffer) const
{
assert(buffer != nullptr);

@ -9,13 +9,13 @@
*
*/
/**
* @file Data.h
* @file NetData.h
* @ingroup dmr
* @file Data.cpp
* @file NetData.cpp
* @ingroup dmr
*/
#if !defined(__DMR_DATA__DATA_H__)
#define __DMR_DATA__DATA_H__
#if !defined(__DMR_DATA__NET_DATA_H__)
#define __DMR_DATA__NET_DATA_H__
#include "common/Defines.h"
#include "common/dmr/DMRDefines.h"
@ -32,27 +32,27 @@ namespace dmr
* @brief Represents network DMR data.
* @ingroup dmr
*/
class HOST_SW_API Data {
class HOST_SW_API NetData {
public:
/**
* @brief Initializes a new instance of the Data class.
* @param data Instance of Data class to copy from.
* @brief Initializes a new instance of the NetData class.
* @param data Instance of NetData class to copy from.
*/
Data(const Data& data);
NetData(const NetData& data);
/**
* @brief Initializes a new instance of the Data class.
* @brief Initializes a new instance of the NetData class.
*/
Data();
NetData();
/**
* @brief Finalizes a instance of the Data class.
* @brief Finalizes a instance of the NetData class.
*/
~Data();
~NetData();
/**
* @brief Equals operator.
* @param data Instance of Data class to copy from.
* @param data Instance of NetData class to copy from.
*/
Data& operator=(const Data& data);
NetData& operator=(const NetData& data);
/**
* @brief Sets raw data.
@ -116,4 +116,4 @@ namespace dmr
} // namespace data
} // namespace dmr
#endif // __DMR_DATA__DATA_H__
#endif // __DMR_DATA__NET_DATA_H__

@ -369,7 +369,7 @@ UInt8Array BaseNetwork::readDMR(bool& ret, uint32_t& frameLength)
/* Writes DMR frame data to the network. */
bool BaseNetwork::writeDMR(const dmr::data::Data& data, bool noSequence)
bool BaseNetwork::writeDMR(const dmr::data::NetData& data, bool noSequence)
{
using namespace dmr::defines;
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
@ -679,7 +679,7 @@ uint16_t BaseNetwork::pktSeq(bool reset)
/* Creates an DMR frame message. */
UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::Data& data)
UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::NetData& data)
{
using namespace dmr::defines;
uint8_t* buffer = new uint8_t[DMR_PACKET_LENGTH + PACKET_PAD];

@ -25,7 +25,7 @@
#define __BASE_NETWORK_H__
#include "common/Defines.h"
#include "common/dmr/data/Data.h"
#include "common/dmr/data/NetData.h"
#include "common/p25/data/DataHeader.h"
#include "common/p25/data/LowSpeedData.h"
#include "common/p25/lc/LC.h"
@ -316,11 +316,11 @@ namespace network
virtual UInt8Array readDMR(bool& ret, uint32_t& frameLength);
/**
* @brief Writes DMR frame data to the network.
* @param[in] data Instance of the dmr::data::Data class containing the DMR message.
* @param[in] data Instance of the dmr::data::NetData class containing the DMR message.
* @param noSequence Flag indicating the message should be sent with no RTP sequence (65535).
* @returns bool True, if message was sent, otherwise false.
*/
virtual bool writeDMR(const dmr::data::Data& data, bool noSequence = false);
virtual bool writeDMR(const dmr::data::NetData& data, bool noSequence = false);
/**
* @brief Helper to test if the DMR ring buffer has data.
@ -513,7 +513,7 @@ namespace network
* @param data Instance of the dmr::data::Data class containing the DMR message.
* @returns UInt8Array Buffer containing the built network message.
*/
UInt8Array createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::Data& data);
UInt8Array createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::NetData& data);
/**
* @brief Creates an P25 frame message header.

@ -81,11 +81,11 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header)
::memset(m_data, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
::memcpy(m_data, buffer + 2U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data
uint8_t crcBuffer[18U];
::memset(crcBuffer, 0x00U, 18U);
uint8_t crcBuffer[P25_PDU_CONFIRMED_LENGTH_BYTES];
::memset(crcBuffer, 0x00U, P25_PDU_CONFIRMED_LENGTH_BYTES);
// generate CRC buffer
for (uint32_t i = 0; i < 144U; i++) {
for (uint32_t i = 0U; i < 144U; i++) {
bool b = READ_BIT(buffer, i);
if (i < 7U) {
WRITE_BIT(crcBuffer, i, b);
@ -153,11 +153,11 @@ void DataBlock::encode(uint8_t* data)
::memcpy(buffer + 2U, m_data, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data
uint8_t crcBuffer[18U];
::memset(crcBuffer, 0x00U, 18U);
uint8_t crcBuffer[P25_PDU_CONFIRMED_LENGTH_BYTES];
::memset(crcBuffer, 0x00U, P25_PDU_CONFIRMED_LENGTH_BYTES);
// generate CRC buffer
for (uint32_t i = 0; i < 144U; i++) {
for (uint32_t i = 0U; i < 144U; i++) {
bool b = READ_BIT(buffer, i);
if (i < 7U) {
WRITE_BIT(crcBuffer, i, b);

@ -36,6 +36,7 @@
// ---------------------------------------------------------------------------
namespace network { namespace callhandler { class HOST_SW_API TagDMRData; } }
namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API DMRPacketData; } } }
namespace network { namespace callhandler { class HOST_SW_API TagP25Data; } }
namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API P25PacketData; } } }
namespace network { namespace callhandler { class HOST_SW_API TagNXDNData; } }
@ -72,6 +73,7 @@ private:
friend class network::FNENetwork;
friend class network::callhandler::TagDMRData;
friend class network::callhandler::packetdata::DMRPacketData;
friend class network::callhandler::TagP25Data;
friend class network::callhandler::packetdata::P25PacketData;
friend class network::callhandler::TagNXDNData;

@ -48,6 +48,7 @@
class HOST_SW_API HostFNE;
class HOST_SW_API RESTAPI;
namespace network { namespace callhandler { class HOST_SW_API TagDMRData; } }
namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API DMRPacketData; } } }
namespace network { namespace callhandler { class HOST_SW_API TagP25Data; } }
namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API P25PacketData; } } }
namespace network { namespace callhandler { class HOST_SW_API TagNXDNData; } }
@ -393,6 +394,7 @@ namespace network
private:
friend class DiagNetwork;
friend class callhandler::TagDMRData;
friend class callhandler::packetdata::DMRPacketData;
callhandler::TagDMRData* m_tagDMR;
friend class callhandler::TagP25Data;
friend class callhandler::packetdata::P25PacketData;

@ -23,6 +23,7 @@
using namespace system_clock;
using namespace network;
using namespace network::callhandler;
using namespace network::callhandler::packetdata;
using namespace dmr;
using namespace dmr::defines;
@ -43,6 +44,8 @@ TagDMRData::TagDMRData(FNENetwork* network, bool debug) :
m_debug(debug)
{
assert(network != nullptr);
m_packetData = new DMRPacketData(network, this, debug);
}
/* Finalizes a instance of the TagDMRData class. */
@ -70,7 +73,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
DataType::E dataType = (DataType::E)(data[15U] & 0x0FU);
data::Data dmrData;
data::NetData dmrData;
dmrData.setSeqNo(seqNo);
dmrData.setSlotNo(slotNo);
dmrData.setSrcId(srcId);
@ -97,14 +100,18 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
dmrData.setN(n);
}
if (dataType == DataType::DATA_HEADER ||
dataType == DataType::RATE_12_DATA ||
dataType == DataType::RATE_34_DATA ||
dataType == DataType::RATE_1_DATA) {
if (dataSync && ((dataType == DataType::DATA_HEADER) ||
(dataType == DataType::RATE_12_DATA) ||
(dataType == DataType::RATE_34_DATA) ||
(dataType == DataType::RATE_1_DATA))) {
if (m_network->m_disablePacketData)
return false;
return m_packetData->processFrame(data, len, peerId, pktSeq, streamId, external);
}
uint8_t frame[DMR_FRAME_LENGTH_BYTES];
dmrData.getData(frame);
// perform TGID route rewrites if configured
routeRewrite(buffer, peerId, dmrData, dataType, dstId, slotNo, false);
dstId = __GET_UINT16(buffer, 8U);
@ -446,7 +453,7 @@ void TagDMRData::write_Call_Alrt(uint32_t peerId, uint8_t slot, uint32_t srcId,
/* Helper to route rewrite the network data buffer. */
void TagDMRData::routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData, DataType::E dataType, uint32_t dstId, uint32_t slotNo, bool outbound)
void TagDMRData::routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::NetData& dmrData, DataType::E dataType, uint32_t dstId, uint32_t slotNo, bool outbound)
{
uint32_t rewriteDstId = dstId;
uint32_t rewriteSlotNo = slotNo;
@ -537,7 +544,7 @@ bool TagDMRData::peerRewrite(uint32_t peerId, uint32_t& dstId, uint32_t& slotNo,
/* Helper to process CSBKs being passed from a peer. */
bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData)
bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetData& dmrData)
{
// are we receiving a CSBK?
if (dmrData.getDataType() == DataType::CSBK) {
@ -598,7 +605,7 @@ bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data&
/* Helper to determine if the peer is permitted for traffic. */
bool TagDMRData::isPeerPermitted(uint32_t peerId, data::Data& data, uint32_t streamId, bool external)
bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t streamId, bool external)
{
if (data.getFLCO() == FLCO::PRIVATE) {
if (!m_network->checkU2UDroppedPeer(peerId))
@ -682,7 +689,7 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::Data& data, uint32_t str
/* Helper to validate the DMR call stream. */
bool TagDMRData::validate(uint32_t peerId, data::Data& data, uint32_t streamId)
bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamId)
{
// is the source ID a blacklisted ID?
lookups::RadioId rid = m_network->m_ridLookup->find(data.getSrcId());
@ -896,7 +903,7 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk)
// Convert the Data Sync to be from the BS or MS as needed
Sync::addDMRDataSync(data + 2U, true);
data::Data dmrData;
data::NetData dmrData;
dmrData.setSlotNo(slot);
dmrData.setDataType(DataType::CSBK);
dmrData.setSrcId(csbk->getSrcId());

@ -18,10 +18,11 @@
#include "fne/Defines.h"
#include "common/dmr/DMRDefines.h"
#include "common/dmr/data/Data.h"
#include "common/dmr/data/NetData.h"
#include "common/dmr/lc/CSBK.h"
#include "common/Clock.h"
#include "network/FNENetwork.h"
#include "network/callhandler/packetdata/DMRPacketData.h"
#include <deque>
@ -140,19 +141,22 @@ namespace network
typedef std::pair<const uint32_t, RxStatus> StatusMapPair;
std::unordered_map<uint32_t, RxStatus> m_status;
friend class packetdata::DMRPacketData;
packetdata::DMRPacketData *m_packetData;
bool m_debug;
/**
* @brief Helper to route rewrite the network data buffer.
* @param buffer Frame buffer.
* @param peerId Peer ID.
* @param dmrData Instance of data::Data DMR data container class.
* @param dmrData Instance of data::NetData DMR data container class.
* @param dataType DMR Data Type.
* @param dstId Destination ID.
* @param slotNo DMR slot number.
* @param outbound Flag indicating whether or not this is outbound traffic.
*/
void routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData, DMRDEF::DataType::E dataType, uint32_t dstId, uint32_t slotNo, bool outbound = true);
void routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::NetData& dmrData, DMRDEF::DataType::E dataType, uint32_t dstId, uint32_t slotNo, bool outbound = true);
/**
* @brief Helper to route rewrite destination ID and slot.
* @param peerId Peer ID.
@ -167,28 +171,28 @@ namespace network
* @brief Helper to process CSBKs being passed from a peer.
* @param buffer Frame buffer.
* @param peerId Peer ID.
* @param dmrData Instance of data::Data DMR data container class.
* @param dmrData Instance of data::NetData DMR data container class.
* @returns bool True, if allowed to pass, otherwise false.
*/
bool processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData);
bool processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetData& dmrData);
/**
* @brief Helper to determine if the peer is permitted for traffic.
* @param peerId Peer ID.
* @param dmrData Instance of data::Data DMR data container class.
* @param dmrData Instance of data::NetData DMR data container class.
* @param streamId Stream ID.
* @param external Flag indicating this traffic came from an external peer.
* @returns bool True, if valid, otherwise false.
*/
bool isPeerPermitted(uint32_t peerId, dmr::data::Data& data, uint32_t streamId, bool external = false);
bool isPeerPermitted(uint32_t peerId, dmr::data::NetData& data, uint32_t streamId, bool external = false);
/**
* @brief Helper to validate the DMR call stream.
* @param peerId Peer ID.
* @param dmrData Instance of data::Data DMR data container class.
* @param dmrData Instance of data::NetData DMR data container class.
* @param streamId Stream ID.
* @returns bool True, if valid, otherwise false.
*/
bool validate(uint32_t peerId, dmr::data::Data& data, uint32_t streamId);
bool validate(uint32_t peerId, dmr::data::NetData& data, uint32_t streamId);
/**
* @brief Helper to write a grant packet.

@ -51,7 +51,7 @@ TagP25Data::TagP25Data(FNENetwork* network, bool debug) :
{
assert(network != nullptr);
m_packetData = new P25PacketData(network, debug);
m_packetData = new P25PacketData(network, this, debug);
}
/* Finalizes a instance of the TagP25Data class. */

@ -161,6 +161,7 @@ namespace network
typedef std::pair<const uint32_t, RxStatus> StatusMapPair;
std::unordered_map<uint32_t, RxStatus> m_status;
friend class packetdata::P25PacketData;
packetdata::P25PacketData *m_packetData;
bool m_debug;

@ -0,0 +1,327 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Converged FNE Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "fne/Defines.h"
#include "common/dmr/data/DataBlock.h"
#include "common/dmr/SlotType.h"
#include "common/dmr/Sync.h"
#include "common/edac/CRC.h"
#include "common/Clock.h"
#include "common/Log.h"
#include "common/Thread.h"
#include "common/Utils.h"
#include "network/FNENetwork.h"
#include "network/callhandler/packetdata/DMRPacketData.h"
#include "HostFNE.h"
using namespace system_clock;
using namespace network;
using namespace network::callhandler;
using namespace network::callhandler::packetdata;
using namespace dmr;
using namespace dmr::defines;
#include <cassert>
#include <chrono>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint8_t DATA_CALL_COLL_TIMEOUT = 60U;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a new instance of the DMRPacketData class. */
DMRPacketData::DMRPacketData(FNENetwork* network, TagDMRData* tag, bool debug) :
m_network(network),
m_tag(tag),
m_status(),
m_debug(debug)
{
assert(network != nullptr);
assert(tag != nullptr);
}
/* Finalizes a instance of the DMRPacketData class. */
DMRPacketData::~DMRPacketData() = default;
/* Process a data frame from the network. */
bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool external)
{
hrc::hrc_t pktTime = hrc::now();
uint8_t seqNo = data[4U];
uint32_t srcId = __GET_UINT16(data, 5U);
uint32_t dstId = __GET_UINT16(data, 8U);
FLCO::E flco = (data[15U] & 0x40U) == 0x40U ? FLCO::PRIVATE : FLCO::GROUP;
uint32_t slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U;
DataType::E dataType = (DataType::E)(data[15U] & 0x0FU);
data::NetData dmrData;
dmrData.setSeqNo(seqNo);
dmrData.setSlotNo(slotNo);
dmrData.setSrcId(srcId);
dmrData.setDstId(dstId);
dmrData.setFLCO(flco);
bool dataSync = (data[15U] & 0x20U) == 0x20U;
bool voiceSync = (data[15U] & 0x10U) == 0x10U;
if (dataSync) {
dmrData.setData(data + 20U);
dmrData.setDataType(dataType);
dmrData.setN(0U);
}
uint8_t frame[DMR_FRAME_LENGTH_BYTES];
dmrData.getData(frame);
// is the stream valid?
if (m_tag->validate(peerId, dmrData, streamId)) {
// is this peer ignored?
if (!m_tag->isPeerPermitted(peerId, dmrData, streamId)) {
return false;
}
auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second->peerId == peerId; });
if (it != m_status.end()) {
RxStatus* status = m_status[peerId];
if (streamId != status->streamId) {
LogWarning(LOG_NET, "DMR, Data Call Collision, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u",
peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, external);
uint64_t duration = hrc::diff(pktTime, status->callStartTime);
if ((duration / 1000) > DATA_CALL_COLL_TIMEOUT) {
LogWarning(LOG_NET, "DMR, force clearing stuck data call, timeout, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxStreamId = %u, external = %u",
peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, external);
delete status;
m_status.erase(peerId);
}
return false;
}
} else {
if (dataSync && (dataType == DataType::DATA_HEADER)) {
// this is a new call stream
RxStatus* status = new RxStatus();
status->callStartTime = pktTime;
status->srcId = srcId;
status->dstId = dstId;
status->slotNo = slotNo;
status->streamId = streamId;
status->peerId = peerId;
bool ret = status->header.decode(frame);
if (!ret) {
LogError(LOG_NET, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", status->slotNo);
Utils::dump(1U, "Unfixable PDU Data", frame, DMR_FRAME_LENGTH_BYTES);
delete status;
m_status.erase(peerId);
return false;
}
status->frames = status->header.getBlocksToFollow();
status->dataBlockCnt = 0U;
bool gi = status->header.getGI();
uint32_t srcId = status->header.getSrcId();
uint32_t dstId = status->header.getDstId();
LogMessage(LOG_NET, DMR_DT_DATA_HEADER ", peerId = %u, slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u",
peerId, status->slotNo, status->header.getDPF(), status->header.getA(), status->header.getSAP(), status->header.getFullMesage(), status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(),
status->header.getFSN(), dstId, srcId, gi);
// make sure we don't get a PDU with more blocks then we support
if (status->header.getBlocksToFollow() >= MAX_PDU_COUNT) {
LogError(LOG_NET, P25_PDU_STR ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), MAX_PDU_COUNT);
return false;
}
m_status[peerId] = status;
LogMessage(LOG_NET, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, status->slotNo, status->srcId, status->dstId, streamId, external);
dispatch(peerId, dmrData, data, len, seqNo, pktSeq, streamId);
return true;
} else {
return false;
}
}
RxStatus* status = m_status[peerId];
// a PDU header only with no blocks to follow is usually a response header
if (status->header.getBlocksToFollow() == 0U) {
LogMessage(LOG_NET, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, external = %u",
peerId, status->slotNo, status->srcId, status->dstId, streamId, external);
delete status;
m_status.erase(peerId);
return true;
}
data::DataBlock dataBlock;
dataBlock.setDataType(dataType);
bool ret = dataBlock.decode(frame, status->header);
if (ret) {
uint32_t len = dataBlock.getData(status->pduUserData + status->pduDataOffset);
status->pduDataOffset += len;
status->frames--;
if (status->frames == 0U)
dataBlock.setLastBlock(true);
if (dataType == DataType::RATE_34_DATA) {
LogMessage(LOG_NET, DMR_DT_RATE_34_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat());
} else if (dataType == DataType::RATE_12_DATA) {
LogMessage(LOG_NET, DMR_DT_RATE_12_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat());
}
else {
LogMessage(LOG_NET, DMR_DT_RATE_1_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat());
}
dispatch(peerId, dmrData, data, len, seqNo, pktSeq, streamId);
status->dataBlockCnt++;
}
// dispatch the PDU data
if (status->dataBlockCnt > 0U && status->frames == 0U) {
if (status->header.getBlocksToFollow() > 0U && status->frames == 0U) {
bool crcRet = edac::CRC::checkCRC32(status->pduUserData, status->pduDataOffset);
if (!crcRet) {
LogWarning(LOG_NET, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", status->header.getBlocksToFollow(), status->pduDataOffset);
}
if (m_network->m_dumpDataPacket) {
Utils::dump(1U, "PDU Packet", status->pduUserData, status->pduDataOffset);
}
}
uint64_t duration = hrc::diff(pktTime, status->callStartTime);
bool gi = status->header.getGI();
uint32_t srcId = status->header.getSrcId();
uint32_t dstId = status->header.getDstId();
LogMessage(LOG_NET, "P25, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, external = %u",
peerId, srcId, dstId, status->header.getBlocksToFollow(), duration / 1000, streamId, external);
// report call event to InfluxDB
if (m_network->m_enableInfluxDB) {
influxdb::QueryBuilder()
.meas("call_event")
.tag("peerId", std::to_string(peerId))
.tag("mode", "DMR")
.tag("streamId", std::to_string(streamId))
.tag("srcId", std::to_string(srcId))
.tag("dstId", std::to_string(dstId))
.field("duration", duration)
.field("slot", slotNo)
.timestamp(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
.request(m_network->m_influxServer);
}
delete status;
m_status.erase(peerId);
}
}
return true;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/* Helper to dispatch PDU user data. */
void DMRPacketData::dispatch(uint32_t peerId, dmr::data::NetData& dmrData, const uint8_t* data, uint32_t len, uint8_t seqNo, uint16_t pktSeq, uint32_t streamId)
{
RxStatus* status = m_status[peerId];
bool gi = status->header.getGI();
uint32_t srcId = status->header.getSrcId();
uint32_t dstId = status->header.getDstId();
// repeat traffic to the connected peers
if (m_network->m_peers.size() > 0U) {
uint32_t i = 0U;
for (auto peer : m_network->m_peers) {
if (peerId != peer.first) {
// is this peer ignored?
if (!m_tag->isPeerPermitted(peer.first, dmrData, status->streamId)) {
continue;
}
// every 5 peers flush the queue
if (i % 5U == 0U) {
m_network->m_frameQueue->flushQueue();
}
m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId, true);
if (m_network->m_debug) {
LogDebug(LOG_NET, "DMR, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u",
peerId, peer.first, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId);
}
if (!m_network->m_callInProgress)
m_network->m_callInProgress = true;
i++;
}
}
m_network->m_frameQueue->flushQueue();
}
// repeat traffic to external peers
if (m_network->m_host->m_peerNetworks.size() > 0U) {
for (auto peer : m_network->m_host->m_peerNetworks) {
uint32_t dstPeerId = peer.second->getPeerId();
// don't try to repeat traffic to the source peer...if this traffic
// is coming from a external peer
if (dstPeerId != peerId) {
// is this peer ignored?
if (!m_tag->isPeerPermitted(dstPeerId, dmrData, status->streamId, true)) {
continue;
}
// check if the source peer is blocked from sending to this peer
if (peer.second->checkBlockedPeer(peerId)) {
continue;
}
// skip peer if it isn't enabled
if (!peer.second->isEnabled()) {
continue;
}
peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId);
if (m_network->m_debug) {
LogDebug(LOG_NET, "DMR, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u",
peerId, dstPeerId, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId);
}
if (!m_network->m_callInProgress)
m_network->m_callInProgress = true;
}
}
}
}

@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Converged FNE Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file DMRPacketData.h
* @ingroup fne_callhandler
* @file DMRPacketData.cpp
* @ingroup fne_callhandler
*/
#if !defined(__PACKETDATA__DMR_PACKET_DATA_H__)
#define __PACKETDATA__DMR_PACKET_DATA_H__
#include "fne/Defines.h"
#include "common/Clock.h"
#include "common/dmr/DMRDefines.h"
#include "common/dmr/data/DataHeader.h"
#include "network/FNENetwork.h"
#include "network/PeerNetwork.h"
#include "network/callhandler/TagDMRData.h"
#include <deque>
namespace network
{
namespace callhandler
{
namespace packetdata
{
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Implements the DMR packet data handler.
* @ingroup fne_callhandler
*/
class HOST_SW_API DMRPacketData {
public:
/**
* @brief Initializes a new instance of the DMRPacketData class.
* @param network Instance of the FNENetwork class.
* @param tag Instance of the TagDMRData class.
* @param debug Flag indicating whether network debug is enabled.
*/
DMRPacketData(FNENetwork* network, TagDMRData* tag, bool debug);
/**
* @brief Finalizes a instance of the P25PacketData class.
*/
~DMRPacketData();
/**
* @brief Process a data frame from the network.
* @param data Network data buffer.
* @param len Length of data.
* @param peerId Peer ID.
* @param pktSeq RTP packet sequence.
* @param streamId Stream ID.
* @param external Flag indicating traffic is from an external peer.
* @returns bool True, if frame is processed, otherwise false.
*/
bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool external = false);
private:
FNENetwork* m_network;
TagDMRData *m_tag;
/**
* @brief Represents the receive status of a call.
*/
class RxStatus {
public:
system_clock::hrc::hrc_t callStartTime;
uint32_t srcId;
uint32_t dstId;
uint8_t slotNo;
uint32_t streamId;
uint32_t peerId;
dmr::data::DataHeader header;
uint8_t dataBlockCnt;
uint8_t frames;
uint8_t* pduUserData;
uint32_t pduDataOffset;
/**
* @brief Initializes a new instance of the RxStatus class
*/
RxStatus() :
srcId(0U),
dstId(0U),
slotNo(0U),
streamId(0U),
peerId(0U),
header(),
dataBlockCnt(0U),
pduUserData(nullptr),
pduDataOffset(0U)
{
pduUserData = new uint8_t[DMRDEF::MAX_PDU_COUNT * DMRDEF::DMR_PDU_UNCODED_LENGTH_BYTES + 2U];
::memset(pduUserData, 0x00U, DMRDEF::MAX_PDU_COUNT * DMRDEF::DMR_PDU_UNCODED_LENGTH_BYTES + 2U);
}
/**
* @brief Finalizes a instance of the RxStatus class
*/
~RxStatus()
{
delete[] pduUserData;
}
};
typedef std::pair<const uint32_t, RxStatus*> StatusMapPair;
std::unordered_map<uint32_t, RxStatus*> m_status;
bool m_debug;
/**
* @brief Helper to dispatch PDU user data.
* @param peerId Peer ID.
* @param dmrData Instance of data::NetData DMR data container class.
* @param data Network data buffer.
* @param len Length of data.
* @param seqNo
* @param pktSeq RTP packet sequence.
* @param streamId Stream ID.
*/
void dispatch(uint32_t peerId, dmr::data::NetData& dmrData, const uint8_t* data, uint32_t len, uint8_t seqNo, uint16_t pktSeq, uint32_t streamId);
};
} // namespace packetdata
} // namespace callhandler
} // namespace network
#endif // __PACKETDATA__DMR_PACKET_DATA_H__

@ -8,7 +8,6 @@
*
*/
#include "fne/Defines.h"
#include "common/p25/lc/tsbk/TSBKFactory.h"
#include "common/p25/Sync.h"
#include "common/edac/CRC.h"
#include "common/Clock.h"
@ -21,6 +20,7 @@
using namespace system_clock;
using namespace network;
using namespace network::callhandler;
using namespace network::callhandler::packetdata;
using namespace p25;
using namespace p25::defines;
@ -40,12 +40,14 @@ const uint8_t DATA_CALL_COLL_TIMEOUT = 60U;
/* Initializes a new instance of the P25PacketData class. */
P25PacketData::P25PacketData(FNENetwork* network, bool debug) :
P25PacketData::P25PacketData(FNENetwork* network, TagP25Data* tag, bool debug) :
m_network(network),
m_tag(tag),
m_status(),
m_debug(debug)
{
assert(network != nullptr);
assert(tag != nullptr);
}
/* Finalizes a instance of the P25PacketData class. */
@ -129,6 +131,29 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee
RxStatus* status = m_status[peerId];
// is the source ID a blacklisted ID?
lookups::RadioId rid = m_network->m_ridLookup->find(status->header.getLLId());
if (!rid.radioDefault()) {
if (!rid.radioEnabled()) {
// report error event to InfluxDB
if (m_network->m_enableInfluxDB) {
influxdb::QueryBuilder()
.meas("call_error_event")
.tag("peerId", std::to_string(peerId))
.tag("streamId", std::to_string(streamId))
.tag("srcId", std::to_string(status->header.getLLId()))
.tag("dstId", std::to_string(status->header.getLLId()))
.field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID)
.timestamp(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
.request(m_network->m_influxServer);
}
delete status;
m_status.erase(peerId);
return false;
}
}
// a PDU header only with no blocks to follow is usually a response header
if (status->header.getBlocksToFollow() == 0U) {
dispatch(peerId);

@ -23,6 +23,7 @@
#include "common/p25/data/DataHeader.h"
#include "network/FNENetwork.h"
#include "network/PeerNetwork.h"
#include "network/callhandler/TagP25Data.h"
#include <deque>
@ -45,9 +46,10 @@ namespace network
/**
* @brief Initializes a new instance of the P25PacketData class.
* @param network Instance of the FNENetwork class.
* @param tag Instance of the TagP25Data class.
* @param debug Flag indicating whether network debug is enabled.
*/
P25PacketData(FNENetwork* network, bool debug);
P25PacketData(FNENetwork* network, TagP25Data* tag, bool debug);
/**
* @brief Finalizes a instance of the P25PacketData class.
*/
@ -67,6 +69,7 @@ namespace network
private:
FNENetwork* m_network;
TagP25Data *m_tag;
/**
* @brief Represents the receive status of a call.

@ -630,7 +630,7 @@ void Control::processNetwork()
return;
}
data::Data data;
data::NetData data;
// process network message header
uint8_t seqNo = buffer[4U];

@ -26,7 +26,7 @@
#define __DMR_CONTROL_H__
#include "Defines.h"
#include "common/dmr/data/Data.h"
#include "common/dmr/data/NetData.h"
#include "common/lookups/RSSIInterpolator.h"
#include "common/lookups/IdenTableLookup.h"
#include "common/lookups/RadioIdLookup.h"

@ -107,11 +107,9 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_permittedDstId(0U),
m_rfLC(nullptr),
m_rfPrivacyLC(nullptr),
m_rfDataHeader(nullptr),
m_rfSeqNo(0U),
m_netLC(nullptr),
m_netPrivacyLC(nullptr),
m_netDataHeader(nullptr),
m_networkWatchdog(1000U, 0U, 1500U),
m_rfTimeoutTimer(1000U, timeout),
m_rfTGHang(1000U, tgHang),
@ -355,7 +353,7 @@ uint32_t Slot::getFrame(uint8_t* data)
/* Process a data frame from the network. */
void Slot::processNetwork(const data::Data& dmrData)
void Slot::processNetwork(const data::NetData& dmrData)
{
// don't process network frames if the RF modem isn't in a listening state
if (m_rfState != RS_RF_LISTENING) {
@ -1183,7 +1181,7 @@ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco,
if (m_network == nullptr)
return;
data::Data dmrData;
data::NetData dmrData;
dmrData.setSlotNo(m_slotNo);
dmrData.setDataType(dataType);
dmrData.setSrcId(srcId);
@ -1251,7 +1249,6 @@ void Slot::writeEndRF(bool writeEnd)
m_rfLC = nullptr;
m_rfPrivacyLC = nullptr;
m_rfDataHeader = nullptr;
}
/* Helper to write network end of frame data. */
@ -1309,7 +1306,6 @@ void Slot::writeEndNet(bool writeEnd)
m_netLC = nullptr;
m_netPrivacyLC = nullptr;
m_netDataHeader = nullptr;
}
/* Helper to write control channel packet data. */

@ -148,7 +148,7 @@ namespace dmr
* @brief Process a data frames from the network.
* @param[in] data Instance of data::Data DMR data container class.
*/
void processNetwork(const data::Data& data);
void processNetwork(const data::NetData& data);
/** @} */
/** @name Data Clocking */
@ -319,13 +319,11 @@ namespace dmr
std::unique_ptr<lc::LC> m_rfLC;
std::unique_ptr<lc::PrivacyLC> m_rfPrivacyLC;
std::unique_ptr<data::DataHeader> m_rfDataHeader;
uint8_t m_rfSeqNo;
std::unique_ptr<lc::LC> m_netLC;
std::unique_ptr<lc::PrivacyLC> m_netPrivacyLC;
std::unique_ptr<data::DataHeader> m_netDataHeader;
Timer m_networkWatchdog;
Timer m_rfTimeoutTimer;

@ -390,7 +390,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len)
/* Process a data frame from the network. */
void ControlSignaling::processNetwork(const data::Data & dmrData)
void ControlSignaling::processNetwork(const data::NetData& dmrData)
{
DataType::E dataType = dmrData.getDataType();
@ -570,7 +570,7 @@ void ControlSignaling::processNetwork(const data::Data & dmrData)
case CSBKO::PRECCSBK:
{
if (m_verbose) {
LogMessage(LOG_NET, "DMR Slot %u, , CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u",
LogMessage(LOG_NET, "DMR Slot %u, CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u",
m_slot->m_slotNo, csbk->getDataContent() ? "Data" : "CSBK", csbk->getCBF(), srcId, dstId);
}
}
@ -597,28 +597,6 @@ void ControlSignaling::processNetwork(const data::Data & dmrData)
data[0U] = modem::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->addFrame(data, true);
}
}
else
m_slot->addFrame(data, true);
}
}

@ -18,7 +18,7 @@
#define __DMR_PACKET_CONTROL_SIGNALING_H__
#include "Defines.h"
#include "common/dmr/data/Data.h"
#include "common/dmr/data/NetData.h"
#include "common/dmr/data/EmbeddedData.h"
#include "common/dmr/lc/LC.h"
#include "common/dmr/lc/CSBK.h"
@ -62,10 +62,10 @@ namespace dmr
bool process(uint8_t* data, uint32_t len);
/**
* @brief Process a data frame from the network.
* @param[in] data Instance of data::Data DMR data container class.
* @param[in] data Instance of data::NetData DMR data container class.
* @returns bool True, if data frame is processed, otherwise false.
*/
void processNetwork(const data::Data& dmrData);
void processNetwork(const data::NetData& dmrData);
/** @} */
/**

@ -15,6 +15,7 @@
#include "common/dmr/SlotType.h"
#include "common/dmr/Sync.h"
#include "common/edac/BPTC19696.h"
#include "common/edac/CRC.h"
#include "common/edac/Trellis.h"
#include "common/Log.h"
#include "common/Utils.h"
@ -151,16 +152,14 @@ bool Data::process(uint8_t* data, uint32_t len)
if (m_slot->m_rfState == RS_RF_DATA)
return true;
data::DataHeader* dataHeader = new data::DataHeader();
bool valid = dataHeader->decode(data + 2U);
m_rfDataHeader.reset();
bool valid = m_rfDataHeader.decode(data + 2U);
if (!valid)
return false;
m_slot->m_rfDataHeader = std::unique_ptr<data::DataHeader>(dataHeader);
bool gi = dataHeader->getGI();
uint32_t srcId = dataHeader->getSrcId();
uint32_t dstId = dataHeader->getDstId();
bool gi = m_rfDataHeader.getGI();
uint32_t srcId = m_rfDataHeader.getSrcId();
uint32_t dstId = m_rfDataHeader.getDstId();
CHECK_AUTHORITATIVE(dstId);
CHECK_TRAFFIC_COLLISION(dstId);
@ -185,29 +184,31 @@ bool Data::process(uint8_t* data, uint32_t len)
}
}
m_slot->m_rfFrames = dataHeader->getBlocks();
m_rfDataBlockCnt = 0U;
m_slot->m_rfFrames = m_rfDataHeader.getBlocksToFollow();
m_slot->m_rfSeqNo = 0U;
m_slot->m_rfLC = std::make_unique<lc::LC>(gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId);
if (m_verbose) {
LogMessage(LOG_RF, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dataHeader->getDPF(), dataHeader->getA(), dataHeader->getSAP(), dataHeader->getFullMesage(), dataHeader->getBlocks(), dataHeader->getPadCount(), dataHeader->getFSN(),
dstId, srcId, gi);
LogMessage(LOG_RF, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, m_rfDataHeader.getDPF(), m_rfDataHeader.getA(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMesage(), m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getPacketLength(),
m_rfDataHeader.getFSN(), dstId, srcId, gi);
}
// did we receive a response header?
if (dataHeader->getDPF() == DPF::RESPONSE) {
if (m_rfDataHeader.getDPF() == DPF::RESPONSE) {
if (m_verbose) {
LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, slot = %u, sap = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dataHeader->getSAP(), dataHeader->getResponseClass(), dataHeader->getResponseType(), dataHeader->getResponseStatus(),
m_slot->m_slotNo, m_rfDataHeader.getSAP(), m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(),
dstId, srcId, gi);
if (dataHeader->getResponseClass() == PDUResponseClass::ACK && dataHeader->getResponseType() == PDUResponseType::ACK) {
if (m_rfDataHeader.getResponseClass() == PDUResponseClass::ACK && m_rfDataHeader.getResponseType() == PDUResponseType::ACK) {
LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP ACK, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
} else {
if (dataHeader->getResponseClass() == PDUResponseClass::NACK) {
switch (dataHeader->getResponseType()) {
if (m_rfDataHeader.getResponseClass() == PDUResponseClass::NACK) {
switch (m_rfDataHeader.getResponseType()) {
case PDUResponseType::NACK_ILLEGAL:
LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, illegal format, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
@ -224,7 +225,7 @@ bool Data::process(uint8_t* data, uint32_t len)
default:
break;
}
} else if (dataHeader->getResponseClass() == PDUResponseClass::ACK_RETRY) {
} else if (m_rfDataHeader.getResponseClass() == PDUResponseClass::ACK_RETRY) {
LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP ACK RETRY, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
}
@ -233,7 +234,7 @@ bool Data::process(uint8_t* data, uint32_t len)
}
// Regenerate the data header
dataHeader->encode(data + 2U);
m_rfDataHeader.encode(data + 2U);
// Regenerate the Slot Type
slotType.encode(data + 2U);
@ -259,7 +260,7 @@ bool Data::process(uint8_t* data, uint32_t len)
::ActivityLog("DMR", true, "Slot %u RF data header from %u to %s%u, %u blocks", m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId, m_slot->m_rfFrames);
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U);
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U);
m_pduDataOffset = 0U;
if (m_slot->m_rfFrames == 0U) {
@ -273,48 +274,43 @@ bool Data::process(uint8_t* data, uint32_t len)
if (m_slot->m_rfState != RS_RF_DATA || m_slot->m_rfFrames == 0U)
return false;
edac::BPTC19696 bptc;
edac::Trellis trellis;
// decode the rate 1/2 payload
if (dataType == DataType::RATE_12_DATA) {
// decode the BPTC (196,96) FEC
uint8_t payload[PDU_UNCONFIRMED_LENGTH_BYTES];
bptc.decode(data + 2U, payload);
// store payload
::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_UNCONFIRMED_LENGTH_BYTES);
m_pduDataOffset += PDU_UNCONFIRMED_LENGTH_BYTES;
data::DataBlock dataBlock;
dataBlock.setDataType(dataType);
// encode the BPTC (196,96) FEC
bptc.encode(payload, data + 2U);
}
else if (dataType == DataType::RATE_34_DATA) {
// decode the Trellis 3/4 rate FEC
uint8_t payload[PDU_CONFIRMED_LENGTH_BYTES];
bool ret = trellis.decode34(data + 2U, payload, true);
bool ret = dataBlock.decode(data + 2U, m_rfDataHeader);
if (ret) {
// store payload
::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_CONFIRMED_LENGTH_BYTES);
uint32_t len = dataBlock.getData(m_pduUserData + m_pduDataOffset);
m_pduDataOffset += len;
m_slot->m_rfFrames--;
if (m_slot->m_rfFrames == 0U)
dataBlock.setLastBlock(true);
// encode the Trellis 3/4 rate FEC
trellis.encode34(payload, data + 2U, true);
if (m_verbose) {
if (dataType == DataType::RATE_34_DATA) {
LogMessage(LOG_RF, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat());
} else if (dataType == DataType::RATE_12_DATA) {
LogMessage(LOG_RF, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat());
}
else {
LogWarning(LOG_RF, "DMR Slot %u, RATE_34_DATA, unfixable RF rate 3/4 data", m_slot->m_slotNo);
Utils::dump(1U, "Unfixable PDU Data", data + 2U, DMR_FRAME_LENGTH_BYTES);
LogMessage(LOG_RF, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat());
}
}
m_pduDataOffset += PDU_CONFIRMED_LENGTH_BYTES;
dataBlock.encode(data + 2U);
m_rfDataBlockCnt++;
}
else if (dataType == DataType::RATE_1_DATA) {
// store payload
::memcpy(m_pduUserData + m_pduDataOffset, data + 2U, PDU_UNCODED_LENGTH_BYTES);
m_pduDataOffset += PDU_UNCODED_LENGTH_BYTES;
if (m_rfDataHeader.getBlocksToFollow() > 0U && m_slot->m_rfFrames == 0U) {
bool crcRet = edac::CRC::checkCRC32(m_pduUserData, m_pduDataOffset);
if (!crcRet) {
LogWarning(LOG_RF, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", m_rfDataHeader.getBlocksToFollow(), m_pduDataOffset);
}
m_slot->m_rfFrames--;
if (m_dumpDataPacket) {
Utils::dump(1U, "PDU Packet", m_pduUserData, m_pduDataOffset);
}
}
data[0U] = m_slot->m_rfFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA;
data[1U] = 0x00U;
@ -332,26 +328,10 @@ bool Data::process(uint8_t* data, uint32_t len)
}
if (m_slot->m_rfFrames == 0U) {
if (m_dumpDataPacket) {
Utils::dump(1U, "PDU Packet", m_pduUserData, m_pduDataOffset);
}
LogMessage(LOG_RF, "DMR Slot %u, RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo);
m_slot->writeEndRF();
}
if (m_verbose) {
if (dataType == DataType::RATE_12_DATA) {
LogMessage(LOG_RF, DMR_DT_RATE_12_DATA ", block = %u", m_slot->m_rfFrames + 1);
}
else if (dataType == DataType::RATE_34_DATA) {
LogMessage(LOG_RF, DMR_DT_RATE_34_DATA ", block = %u", m_slot->m_rfFrames + 1);
}
else {
LogMessage(LOG_RF, DMR_DT_RATE_1_DATA ", block = %u", m_slot->m_rfFrames + 1);
}
}
return true;
}
@ -360,9 +340,9 @@ bool Data::process(uint8_t* data, uint32_t len)
/* Process a data frame from the network. */
void Data::processNetwork(const data::Data& dmrData)
void Data::processNetwork(const data::NetData& dmrData)
{
uint8_t dataType = dmrData.getDataType();
DataType::E dataType = dmrData.getDataType();
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
dmrData.getData(data + 2U);
@ -424,18 +404,16 @@ void Data::processNetwork(const data::Data& dmrData)
if (m_slot->m_netState == RS_NET_DATA)
return;
data::DataHeader* dataHeader = new data::DataHeader();
bool valid = dataHeader->decode(data + 2U);
m_netDataHeader.reset();
bool valid = m_netDataHeader.decode(data + 2U);
if (!valid) {
LogError(LOG_NET, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", m_slot->m_slotNo);
return;
}
m_slot->m_netDataHeader = std::unique_ptr<data::DataHeader>(dataHeader);
bool gi = dataHeader->getGI();
uint32_t srcId = dataHeader->getSrcId();
uint32_t dstId = dataHeader->getDstId();
bool gi = m_netDataHeader.getGI();
uint32_t srcId = m_netDataHeader.getSrcId();
uint32_t dstId = m_netDataHeader.getDstId();
CHECK_NET_AUTHORITATIVE(dstId);
CHECK_TG_HANG(dstId);
@ -444,22 +422,24 @@ void Data::processNetwork(const data::Data& dmrData)
m_slot->m_tsccPayloadActRetry.stop();
}
m_slot->m_netFrames = dataHeader->getBlocks();
m_netDataBlockCnt = 0U;
m_slot->m_netFrames = m_netDataHeader.getBlocksToFollow();
m_slot->m_netLC = std::make_unique<lc::LC>(gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId);
// did we receive a response header?
if (dataHeader->getDPF() == DPF::RESPONSE) {
if (m_netDataHeader.getDPF() == DPF::RESPONSE) {
if (m_verbose) {
LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, slot = %u, sap = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dataHeader->getSAP(), dataHeader->getResponseClass(), dataHeader->getResponseType(), dataHeader->getResponseStatus(),
m_slot->m_slotNo, m_netDataHeader.getSAP(), m_netDataHeader.getResponseClass(), m_netDataHeader.getResponseType(), m_netDataHeader.getResponseStatus(),
dstId, srcId, gi);
if (dataHeader->getResponseClass() == PDUResponseClass::ACK && dataHeader->getResponseType() == PDUResponseType::ACK) {
if (m_netDataHeader.getResponseClass() == PDUResponseClass::ACK && m_netDataHeader.getResponseType() == PDUResponseType::ACK) {
LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP ACK, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
} else {
if (dataHeader->getResponseClass() == PDUResponseClass::NACK) {
switch (dataHeader->getResponseType()) {
if (m_netDataHeader.getResponseClass() == PDUResponseClass::NACK) {
switch (m_netDataHeader.getResponseType()) {
case PDUResponseType::NACK_ILLEGAL:
LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, illegal format, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
@ -476,7 +456,7 @@ void Data::processNetwork(const data::Data& dmrData)
default:
break;
}
} else if (dataHeader->getResponseClass() == PDUResponseClass::ACK_RETRY) {
} else if (m_netDataHeader.getResponseClass() == PDUResponseClass::ACK_RETRY) {
LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP ACK RETRY, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
}
@ -485,7 +465,7 @@ void Data::processNetwork(const data::Data& dmrData)
}
// Regenerate the data header
dataHeader->encode(data + 2U);
m_netDataHeader.encode(data + 2U);
// Regenerate the Slot Type
SlotType slotType;
@ -512,15 +492,15 @@ void Data::processNetwork(const data::Data& dmrData)
m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::DATA);
if (m_verbose) {
LogMessage(LOG_NET, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dataHeader->getDPF(), dataHeader->getA(), dataHeader->getSAP(), dataHeader->getFullMesage(), dataHeader->getBlocks(), dataHeader->getPadCount(), dataHeader->getFSN(),
dstId, srcId, gi);
LogMessage(LOG_NET, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, m_netDataHeader.getDPF(), m_netDataHeader.getA(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMesage(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getPacketLength(),
m_netDataHeader.getFSN(), dstId, srcId, gi);
}
::ActivityLog("DMR", false, "Slot %u network data header from %u to %s%u, %u blocks",
m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId, m_slot->m_netFrames);
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U);
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U);
m_pduDataOffset = 0U;
if (m_slot->m_netFrames == 0U) {
@ -534,47 +514,43 @@ void Data::processNetwork(const data::Data& dmrData)
return;
}
// regenerate the rate 1/2 payload
if (dataType == DataType::RATE_12_DATA) {
// decode the BPTC (196,96) FEC
edac::BPTC19696 bptc;
uint8_t payload[PDU_UNCONFIRMED_LENGTH_BYTES];
bptc.decode(data + 2U, payload);
// store payload
::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_UNCONFIRMED_LENGTH_BYTES);
m_pduDataOffset += PDU_UNCONFIRMED_LENGTH_BYTES;
data::DataBlock dataBlock;
dataBlock.setDataType(dataType);
// encode the BPTC (196,96) FEC
bptc.encode(payload, data + 2U);
}
else if (dataType == DataType::RATE_34_DATA) {
// decode the Trellis 3/4 rate FEC
edac::Trellis trellis;
uint8_t payload[PDU_CONFIRMED_LENGTH_BYTES];
bool ret = trellis.decode34(data + 2U, payload, true);
bool ret = dataBlock.decode(data + 2U, m_netDataHeader);
if (ret) {
// store payload
::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_CONFIRMED_LENGTH_BYTES);
uint32_t len = dataBlock.getData(m_pduUserData + m_pduDataOffset);
m_pduDataOffset += len;
m_slot->m_netFrames--;
if (m_slot->m_netFrames == 0U)
dataBlock.setLastBlock(true);
// encode the Trellis 3/4 rate FEC
trellis.encode34(payload, data + 2U, true);
if (m_verbose) {
if (dataType == DataType::RATE_34_DATA) {
LogMessage(LOG_NET, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat());
} else if (dataType == DataType::RATE_12_DATA) {
LogMessage(LOG_NET, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat());
}
else {
LogWarning(LOG_NET, "DMR Slot %u, RATE_34_DATA, unfixable network rate 3/4 data", m_slot->m_slotNo);
Utils::dump(1U, "Data", data + 2U, DMR_FRAME_LENGTH_BYTES);
LogMessage(LOG_NET, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat());
}
}
m_pduDataOffset += PDU_CONFIRMED_LENGTH_BYTES;
dataBlock.encode(data + 2U);
m_netDataBlockCnt++;
}
else if (dataType == DataType::RATE_1_DATA) {
// store payload
::memcpy(m_pduUserData + m_pduDataOffset, data + 2U, PDU_UNCODED_LENGTH_BYTES);
m_pduDataOffset += PDU_UNCODED_LENGTH_BYTES;
if (m_netDataHeader.getBlocksToFollow() > 0U && m_slot->m_netFrames == 0U) {
bool crcRet = edac::CRC::checkCRC32(m_pduUserData, m_pduDataOffset);
if (!crcRet) {
LogWarning(LOG_NET, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", m_netDataHeader.getBlocksToFollow(), m_pduDataOffset);
}
m_slot->m_netFrames--;
if (m_dumpDataPacket) {
Utils::dump(1U, "PDU Packet", m_pduUserData, m_pduDataOffset);
}
}
if (m_repeatDataPacket) {
// regenerate the Slot Type
@ -590,25 +566,9 @@ void Data::processNetwork(const data::Data& dmrData)
data[1U] = 0x00U;
m_slot->addFrame(data, true);
if (m_verbose) {
if (dataType == DataType::RATE_12_DATA) {
LogMessage(LOG_RF, DMR_DT_RATE_12_DATA ", block = %u", m_slot->m_netFrames + 1);
}
else if (dataType == DataType::RATE_34_DATA) {
LogMessage(LOG_RF, DMR_DT_RATE_34_DATA ", block = %u", m_slot->m_netFrames + 1);
}
else {
LogMessage(LOG_RF, DMR_DT_RATE_1_DATA ", block = %u", m_slot->m_netFrames + 1);
}
}
}
if (m_slot->m_netFrames == 0U) {
if (m_dumpDataPacket) {
Utils::dump(1U, "PDU Packet", m_pduUserData, m_pduDataOffset);
}
LogMessage(LOG_NET, "DMR Slot %u, RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo);
m_slot->writeEndNet();
}
@ -627,6 +587,10 @@ void Data::processNetwork(const data::Data& dmrData)
Data::Data(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) :
m_slot(slot),
m_rfDataHeader(),
m_rfDataBlockCnt(0U),
m_netDataHeader(),
m_netDataBlockCnt(0U),
m_pduUserData(nullptr),
m_pduDataOffset(0U),
m_lastRejectId(0U),
@ -635,8 +599,8 @@ Data::Data(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool
m_verbose(verbose),
m_debug(debug)
{
m_pduUserData = new uint8_t[MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U];
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U);
m_pduUserData = new uint8_t[MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U];
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U);
}
/* Finalizes a instance of the Data class. */
@ -709,7 +673,7 @@ void Data::writeRF_PDU_Ack_Response(uint8_t rspClass, uint8_t rspType, uint8_t r
rspHeader.setResponseClass(rspClass);
rspHeader.setResponseType(rspType);
rspHeader.setResponseStatus(rspStatus);
rspHeader.setBlocks(1U);
rspHeader.setBlocksToFollow(1U);
rspHeader.encode(data + 2U);
writeRF_PDU(DataType::DATA_HEADER, data);
@ -717,8 +681,8 @@ void Data::writeRF_PDU_Ack_Response(uint8_t rspClass, uint8_t rspType, uint8_t r
::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES);
// decode the BPTC (196,96) FEC
uint8_t payload[PDU_UNCONFIRMED_LENGTH_BYTES];
::memset(payload, 0x00U, PDU_UNCONFIRMED_LENGTH_BYTES);
uint8_t payload[DMR_PDU_UNCONFIRMED_LENGTH_BYTES];
::memset(payload, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES);
// encode the BPTC (196,96) FEC
bptc.encode(payload, data + 2U);

@ -18,8 +18,9 @@
#define __DMR_PACKET_DATA_H__
#include "Defines.h"
#include "common/dmr/data/Data.h"
#include "common/dmr/data/NetData.h"
#include "common/dmr/data/DataHeader.h"
#include "common/dmr/data/DataBlock.h"
#include "common/dmr/data/EmbeddedData.h"
#include "common/dmr/lc/LC.h"
#include "common/edac/AMBEFEC.h"
@ -63,10 +64,10 @@ namespace dmr
bool process(uint8_t* data, uint32_t len);
/**
* @brief Process a data frame from the network.
* @param[in] data Instance of data::Data DMR data container class.
* @param[in] data Instance of data::NetData DMR data container class.
* @returns bool True, if data frame is processed, otherwise false.
*/
void processNetwork(const data::Data& dmrData);
void processNetwork(const data::NetData& dmrData);
/** @} */
private:
@ -75,6 +76,12 @@ namespace dmr
friend class dmr::Slot;
Slot* m_slot;
data::DataHeader m_rfDataHeader;
uint8_t m_rfDataBlockCnt;
data::DataHeader m_netDataHeader;
uint8_t m_netDataBlockCnt;
uint8_t* m_pduUserData;
uint32_t m_pduDataOffset;
uint32_t m_lastRejectId;

@ -632,7 +632,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
/* Process a voice frame from the network. */
void Voice::processNetwork(const data::Data& dmrData)
void Voice::processNetwork(const data::NetData& dmrData)
{
uint8_t dataType = dmrData.getDataType();

@ -18,7 +18,7 @@
#define __DMR_PACKET_VOICE_H__
#include "Defines.h"
#include "common/dmr/data/Data.h"
#include "common/dmr/data/NetData.h"
#include "common/dmr/data/EmbeddedData.h"
#include "common/dmr/lc/LC.h"
#include "common/dmr/lc/PrivacyLC.h"
@ -58,10 +58,10 @@ namespace dmr
bool process(uint8_t* data, uint32_t len);
/**
* @brief Process a data frame from the network.
* @param[in] data Instance of data::Data DMR data container class.
* @param[in] data Instance of data::NetData DMR data container class.
* @returns bool True, if data frame is processed, otherwise false.
*/
void processNetwork(const data::Data& dmrData);
void processNetwork(const data::NetData& dmrData);
/** @} */
private:

Loading…
Cancel
Save

Powered by TurnKey Linux.