correct Trellis symbol skipping logic for DMR for 3/4 Trellis; enhance DMR short LC bits; correctly identify short LC for data and CSBK; correct buffered PDU repeat logic (when header has no blocks following);

pull/61/head
Bryan Biedenkapp 2 years ago
parent 1e25fff1ed
commit 197ce833ac

@ -109,7 +109,10 @@ namespace dmr
const uint8_t SLOT2 = 0x80U;
const uint32_t MAX_PDU_COUNT = 32U;
const uint32_t MAX_PDU_LENGTH = 512U;
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 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
/** @} */

@ -37,6 +37,7 @@ const uint8_t UDTF_NMEA = 0x05U;
DataHeader::DataHeader() :
m_GI(false),
m_A(false),
m_DPF(DPF::UDT),
m_sap(0U),
m_fsn(0U),
@ -54,7 +55,6 @@ DataHeader::DataHeader() :
m_srcPort(0U),
m_dstPort(0U),
m_data(nullptr),
m_A(false),
m_SF(false),
m_PF(false),
m_UDTO(0U)

@ -62,9 +62,13 @@ namespace dmr
public:
/**
* @brief Flag indicating whether the CSBK is group or individual.
* @brief Flag indicating whether the data header is group or individual.
*/
__PROPERTY(bool, GI, GI);
/**
* @brief Flag indicating whether the data header requires acknowledgement.
*/
__PROPERTY(bool, A, A);
/**
* @brief Data packet format.
@ -120,15 +124,15 @@ namespace dmr
/**
* @brief Response class.
*/
__PROPERTY(uint8_t, rspClass, Class);
__PROPERTY(uint8_t, rspClass, ResponseClass);
/**
* @brief Response type.
*/
__PROPERTY(uint8_t, rspType, Type);
__PROPERTY(uint8_t, rspType, ResponseType);
/**
* @brief Response status.
*/
__PROPERTY(uint8_t, rspStatus, Status);
__PROPERTY(uint8_t, rspStatus, ResponseStatus);
/**
* @brief Source Port.
@ -141,7 +145,6 @@ namespace dmr
private:
uint8_t* m_data;
bool m_A;
bool m_SF;
bool m_PF;
uint8_t m_UDTO;

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2016,2018 Jonathan Naylor, G4KLX
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -57,13 +57,13 @@ Trellis::~Trellis() = default;
/* Decodes 3/4 rate Trellis. */
bool Trellis::decode34(const uint8_t* data, uint8_t* payload)
bool Trellis::decode34(const uint8_t* data, uint8_t* payload, bool skipSymbols)
{
assert(data != nullptr);
assert(payload != nullptr);
int8_t dibits[98U];
deinterleave(data, dibits);
deinterleave(data, dibits, skipSymbols);
uint8_t points[49U];
dibitsToPoints(dibits, points);
@ -93,7 +93,7 @@ bool Trellis::decode34(const uint8_t* data, uint8_t* payload)
/* Encodes 3/4 rate Trellis. */
void Trellis::encode34(const uint8_t* payload, uint8_t* data)
void Trellis::encode34(const uint8_t* payload, uint8_t* data, bool skipSymbols)
{
assert(payload != nullptr);
assert(data != nullptr);
@ -115,7 +115,7 @@ void Trellis::encode34(const uint8_t* payload, uint8_t* data)
int8_t dibits[98U];
pointsToDibits(points, dibits);
interleave(dibits, data);
interleave(dibits, data, skipSymbols);
}
/* Decodes 1/2 rate Trellis. */
@ -187,13 +187,15 @@ void Trellis::encode12(const uint8_t* payload, uint8_t* data)
/* Helper to deinterleave the input symbols into dibits. */
void Trellis::deinterleave(const uint8_t* data, int8_t* dibits) const
void Trellis::deinterleave(const uint8_t* data, int8_t* dibits, bool skipSymbols) const
{
for (uint32_t i = 0U; i < 98U; i++) {
uint32_t n = i * 2U + 0U;
if (skipSymbols && n >= 98U) n += 68U;
bool b1 = READ_BIT(data, n) != 0x00U;
n = i * 2U + 1U;
if (skipSymbols && n >= 98U) n += 68U;
bool b2 = READ_BIT(data, n) != 0x00U;
int8_t dibit;
@ -213,7 +215,7 @@ void Trellis::deinterleave(const uint8_t* data, int8_t* dibits) const
/* Helper to interleave the input dibits into symbols. */
void Trellis::interleave(const int8_t* dibits, uint8_t* data) const
void Trellis::interleave(const int8_t* dibits, uint8_t* data, bool skipSymbols) const
{
for (uint32_t i = 0U; i < 98U; i++) {
uint32_t n = INTERLEAVE_TABLE[i];
@ -239,9 +241,11 @@ void Trellis::interleave(const int8_t* dibits, uint8_t* data) const
}
n = i * 2U + 0U;
if (skipSymbols && n >= 98U) n += 68U;
WRITE_BIT(data, n, b1);
n = i * 2U + 1U;
if (skipSymbols && n >= 98U) n += 68U;
WRITE_BIT(data, n, b2);
}
}

@ -5,6 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2016,2018 Jonathan Naylor, G4KLX
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -43,15 +44,17 @@ namespace edac
* @brief Decodes 3/4 rate Trellis.
* @param[in] data Trellis symbol bytes.
* @param[out] payload Output bytes.
* @param skipSymbols Flag indicating symbols should be skipped (this is used for DMR).
* @returns bool True, if Trellis decoded, otherwise false.
*/
bool decode34(const uint8_t* data, uint8_t* payload);
bool decode34(const uint8_t* data, uint8_t* payload, bool skipSymbols = false);
/**
* @brief Encodes 3/4 rate Trellis.
* @param[in] payload Input bytes.
* @param[out] data Trellis symbol bytes.
* @param skipSymbols Flag indicating symbols should be skipped (this is used for DMR).
*/
void encode34(const uint8_t* payload, uint8_t* data);
void encode34(const uint8_t* payload, uint8_t* data, bool skipSymbols = false);
/**
* @brief Decodes 1/2 rate Trellis.
@ -72,14 +75,16 @@ namespace edac
* @brief Helper to deinterleave the input symbols into dibits.
* @param[in] data Trellis symbol bytes.
* @param[out] dibits Dibits.
* @param skipSymbols Flag indicating symbols should be skipped (this is used for DMR).
*/
void deinterleave(const uint8_t* in, int8_t* dibits) const;
void deinterleave(const uint8_t* in, int8_t* dibits, bool skipSymbols = false) const;
/**
* @brief Helper to interleave the input dibits into symbols.
* @param[in] dibits Dibits.
* @param[out] data Trellis symbol bytes.
* @param skipSymbols Flag indicating symbols should be skipped (this is used for DMR).
*/
void interleave(const int8_t* dibits, uint8_t* out) const;
void interleave(const int8_t* dibits, uint8_t* out, bool skipSymbols = false) const;
/**
* @brief Helper to map dibits to trellis constellation points.
* @param[in] dibits Dibits.

@ -164,8 +164,11 @@ int Host::run()
// initialize system logging
yaml::Node logConf = m_conf["log"];
bool useSyslog = logConf["useSyslog"].as<bool>(false);
if (g_foreground)
useSyslog = false;
ret = ::LogInitialise(logConf["filePath"].as<std::string>(), logConf["fileRoot"].as<std::string>(),
logConf["fileLevel"].as<uint32_t>(0U), logConf["displayLevel"].as<uint32_t>(0U), false, logConf["useSyslog"].as<bool>(false));
logConf["fileLevel"].as<uint32_t>(0U), logConf["displayLevel"].as<uint32_t>(0U), false, useSyslog);
if (!ret) {
::fatal("unable to open the log file\n");
}

@ -70,10 +70,10 @@ uint8_t* Slot::m_idle = nullptr;
FLCO::E Slot::m_flco1;
uint8_t Slot::m_id1 = 0U;
bool Slot::m_voice1 = true;
Slot::SLCO_ACT_TYPE Slot::m_actType1 = Slot::SLCO_ACT_TYPE::VOICE;
FLCO::E Slot::m_flco2;
uint8_t Slot::m_id2 = 0U;
bool Slot::m_voice2 = true;
Slot::SLCO_ACT_TYPE Slot::m_actType2 = Slot::SLCO_ACT_TYPE::VOICE;
bool Slot::m_verifyReg = false;
@ -516,7 +516,10 @@ void Slot::clock()
}
if ((m_dmr->m_tsccCnt % 2) > 0) {
setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO::GROUP : FLCO::PRIVATE, m_tsccPayloadVoice);
if (m_tsccPayloadVoice)
setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO::GROUP : FLCO::PRIVATE, SLCO_ACT_TYPE::VOICE);
else
setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO::GROUP : FLCO::PRIVATE, SLCO_ACT_TYPE::DATA);
}
}
}
@ -1452,7 +1455,7 @@ void Slot::clearTSCCActivated()
/* Helper to set the DMR short LC. */
void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, bool voice)
void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, SLCO_ACT_TYPE actType)
{
assert(m_modem != nullptr);
@ -1460,7 +1463,7 @@ void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, bool voice)
case 1U:
m_id1 = 0U;
m_flco1 = flco;
m_voice1 = voice;
m_actType1 = actType;
if (id != 0U) {
uint8_t buffer[3U];
buffer[0U] = (id << 16) & 0xFFU;
@ -1472,7 +1475,7 @@ void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, bool voice)
case 2U:
m_id2 = 0U;
m_flco2 = flco;
m_voice2 = voice;
m_actType2 = actType;
if (id != 0U) {
uint8_t buffer[3U];
buffer[0U] = (id << 16) & 0xFFU;
@ -1498,34 +1501,34 @@ void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, bool voice)
if (m_id1 != 0U) {
lc[2U] = m_id1;
if (m_voice1) {
if (m_flco1 == FLCO::GROUP)
lc[1U] |= 0x80U;
else
lc[1U] |= 0x90U;
}
else {
if (m_flco1 == FLCO::GROUP)
lc[1U] |= 0xB0U;
else
lc[1U] |= 0xA0U;
}
if (m_actType1 == SLCO_ACT_TYPE::VOICE && m_flco1 == FLCO::GROUP)
lc[1U] |= 0x08U;
else if (m_actType1 == SLCO_ACT_TYPE::VOICE && m_flco1 == FLCO::PRIVATE)
lc[1U] |= 0x09U;
else if (m_actType1 == SLCO_ACT_TYPE::DATA && m_flco1 == FLCO::GROUP)
lc[1U] |= 0x0BU;
else if (m_actType1 == SLCO_ACT_TYPE::DATA && m_flco1 == FLCO::PRIVATE)
lc[1U] |= 0x0AU;
else if (m_actType1 == SLCO_ACT_TYPE::CSBK && m_flco1 == FLCO::GROUP)
lc[1U] |= 0x02U;
else if (m_actType1 == SLCO_ACT_TYPE::CSBK && m_flco1 == FLCO::PRIVATE)
lc[1U] |= 0x03U;
}
if (m_id2 != 0U) {
lc[3U] = m_id2;
if (m_voice2) {
if (m_flco2 == FLCO::GROUP)
lc[1U] |= 0x08U;
else
lc[1U] |= 0x09U;
}
else {
if (m_flco2 == FLCO::GROUP)
lc[1U] |= 0x0BU;
else
lc[1U] |= 0x0AU;
}
if (m_actType2 == SLCO_ACT_TYPE::VOICE && m_flco2 == FLCO::GROUP)
lc[1U] |= 0x08U;
else if (m_actType2 == SLCO_ACT_TYPE::VOICE && m_flco2 == FLCO::PRIVATE)
lc[1U] |= 0x09U;
else if (m_actType2 == SLCO_ACT_TYPE::DATA && m_flco2 == FLCO::GROUP)
lc[1U] |= 0x0BU;
else if (m_actType2 == SLCO_ACT_TYPE::DATA && m_flco2 == FLCO::PRIVATE)
lc[1U] |= 0x0AU;
else if (m_actType2 == SLCO_ACT_TYPE::CSBK && m_flco2 == FLCO::GROUP)
lc[1U] |= 0x02U;
else if (m_actType2 == SLCO_ACT_TYPE::CSBK && m_flco2 == FLCO::PRIVATE)
lc[1U] |= 0x03U;
}
lc[4U] = edac::CRC::crc8(lc, 4U);

@ -430,13 +430,23 @@ namespace dmr
static uint8_t* m_idle;
/**
* @brief Short LC Activity Type
*/
enum SLCO_ACT_TYPE {
NONE, //! None
VOICE, //! Voice
DATA, //! Data
CSBK //! CSBK
};
static defines::FLCO::E m_flco1;
static uint8_t m_id1;
static bool m_voice1;
static SLCO_ACT_TYPE m_actType1;
static defines::FLCO::E m_flco2;
static uint8_t m_id2;
static bool m_voice2;
static SLCO_ACT_TYPE m_actType2;
static bool m_verifyReg;
@ -517,9 +527,9 @@ namespace dmr
* @param slotNo DMR slot number.
* @param id ID.
* @param flco Full-Link Control Opcode.
* @param voice Flag indicating this is for a voice frame.
* @param actType Traffic activity type.
*/
static void setShortLC(uint32_t slotNo, uint32_t id, defines::FLCO::E flco = defines::FLCO::GROUP, bool voice = true);
static void setShortLC(uint32_t slotNo, uint32_t id, defines::FLCO::E flco = defines::FLCO::GROUP, Slot::SLCO_ACT_TYPE actType = Slot::SLCO_ACT_TYPE::NONE);
/**
* @brief Helper to set the DMR short LC for TSCC.
* @param siteData Instance of the dmr::SiteData class.

@ -171,6 +171,13 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len)
}
}
// if data preamble, signal its existence
if (m_slot->m_netState == RS_NET_IDLE && csbk->getDataContent()) {
m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::DATA);
} else {
m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::CSBK);
}
bool handled = false;
switch (csbko) {
case CSBKO::UU_V_REQ:
@ -343,7 +350,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len)
case CSBKO::PRECCSBK:
{
if (m_verbose) {
LogMessage(LOG_RF, "DMR Slot %u, , CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u",
LogMessage(LOG_RF, "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);
}
}
@ -435,11 +442,19 @@ void ControlSignaling::processNetwork(const data::Data & dmrData)
}
}
bool gi = csbk->getGI();
uint32_t srcId = csbk->getSrcId();
uint32_t dstId = csbk->getDstId();
CHECK_TG_HANG(dstId);
// if data preamble, signal its existence
if (csbk->getDataContent()) {
m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::DATA);
} else {
m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::CSBK);
}
bool handled = false;
switch (csbko) {
case CSBKO::UU_V_REQ:

@ -189,6 +189,49 @@ bool Data::process(uint8_t* data, uint32_t len)
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);
}
// did we receive a response header?
if (dataHeader->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(),
dstId, srcId, gi);
if (dataHeader->getResponseClass() == PDUResponseClass::ACK && dataHeader->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()) {
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);
break;
case PDUResponseType::NACK_PACKET_CRC:
LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet CRC error, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
break;
case PDUResponseType::NACK_UNDELIVERABLE:
LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet undeliverable, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
break;
default:
break;
}
} else if (dataHeader->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);
}
}
}
}
// Regenerate the data header
dataHeader->encode(data + 2U);
@ -211,18 +254,12 @@ bool Data::process(uint8_t* data, uint32_t len)
m_slot->m_rfLastSrcId = srcId;
if (m_slot->m_netState == RS_NET_IDLE) {
m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, false);
}
if (m_verbose) {
LogMessage(LOG_RF, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dataHeader->getDPF(), dataHeader->getSAP(), dataHeader->getFullMesage(), dataHeader->getBlocks(), dataHeader->getPadCount(), dataHeader->getFSN(),
dstId, srcId, gi);
m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::DATA);
}
::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 * MAX_PDU_LENGTH + 2U);
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U);
m_pduDataOffset = 0U;
if (m_slot->m_rfFrames == 0U) {
@ -242,33 +279,39 @@ bool Data::process(uint8_t* data, uint32_t len)
// decode the rate 1/2 payload
if (dataType == DataType::RATE_12_DATA) {
// decode the BPTC (196,96) FEC
uint8_t payload[12U];
uint8_t payload[PDU_UNCONFIRMED_LENGTH_BYTES];
bptc.decode(data + 2U, payload);
// store payload
::memcpy(m_pduUserData, payload, 12U);
m_pduDataOffset += 12U;
::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_UNCONFIRMED_LENGTH_BYTES);
m_pduDataOffset += PDU_UNCONFIRMED_LENGTH_BYTES;
// 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[18U];
bool ret = trellis.decode34(data + 2U, payload);
uint8_t payload[PDU_CONFIRMED_LENGTH_BYTES];
bool ret = trellis.decode34(data + 2U, payload, true);
if (ret) {
// store payload
::memcpy(m_pduUserData, payload, 18U);
::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_CONFIRMED_LENGTH_BYTES);
// encode the Trellis 3/4 rate FEC
trellis.encode34(payload, data + 2U);
trellis.encode34(payload, data + 2U, true);
}
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);
}
m_pduDataOffset += 18U;
m_pduDataOffset += PDU_CONFIRMED_LENGTH_BYTES;
}
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;
}
m_slot->m_rfFrames--;
@ -404,6 +447,43 @@ void Data::processNetwork(const data::Data& dmrData)
m_slot->m_netFrames = dataHeader->getBlocks();
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_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(),
dstId, srcId, gi);
if (dataHeader->getResponseClass() == PDUResponseClass::ACK && dataHeader->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()) {
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);
break;
case PDUResponseType::NACK_PACKET_CRC:
LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet CRC error, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
break;
case PDUResponseType::NACK_UNDELIVERABLE:
LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet undeliverable, slot = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dstId, srcId, gi);
break;
default:
break;
}
} else if (dataHeader->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);
}
}
}
}
// Regenerate the data header
dataHeader->encode(data + 2U);
@ -429,18 +509,18 @@ void Data::processNetwork(const data::Data& dmrData)
m_slot->m_netLastDstId = dstId;
m_slot->m_netLastSrcId = srcId;
m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, false);
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, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u",
m_slot->m_slotNo, dataHeader->getDPF(), dataHeader->getSAP(), dataHeader->getFullMesage(), dataHeader->getBlocks(), dataHeader->getPadCount(), dataHeader->getFSN(),
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);
}
::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 * MAX_PDU_LENGTH + 2U);
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U);
m_pduDataOffset = 0U;
if (m_slot->m_netFrames == 0U) {
@ -458,12 +538,12 @@ void Data::processNetwork(const data::Data& dmrData)
if (dataType == DataType::RATE_12_DATA) {
// decode the BPTC (196,96) FEC
edac::BPTC19696 bptc;
uint8_t payload[12U];
uint8_t payload[PDU_UNCONFIRMED_LENGTH_BYTES];
bptc.decode(data + 2U, payload);
// store payload
::memcpy(m_pduUserData, payload, 12U);
m_pduDataOffset += 12U;
::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_UNCONFIRMED_LENGTH_BYTES);
m_pduDataOffset += PDU_UNCONFIRMED_LENGTH_BYTES;
// encode the BPTC (196,96) FEC
bptc.encode(payload, data + 2U);
@ -471,21 +551,27 @@ void Data::processNetwork(const data::Data& dmrData)
else if (dataType == DataType::RATE_34_DATA) {
// decode the Trellis 3/4 rate FEC
edac::Trellis trellis;
uint8_t payload[18U];
bool ret = trellis.decode34(data + 2U, payload);
uint8_t payload[PDU_CONFIRMED_LENGTH_BYTES];
bool ret = trellis.decode34(data + 2U, payload, true);
if (ret) {
// store payload
::memcpy(m_pduUserData, payload, 18U);
::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_CONFIRMED_LENGTH_BYTES);
// encode the Trellis 3/4 rate FEC
trellis.encode34(payload, data + 2U);
trellis.encode34(payload, data + 2U, true);
}
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);
}
m_pduDataOffset += 18U;
m_pduDataOffset += PDU_CONFIRMED_LENGTH_BYTES;
}
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;
}
m_slot->m_netFrames--;
@ -549,8 +635,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 * MAX_PDU_LENGTH + 2U];
::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * MAX_PDU_LENGTH + 2U);
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);
}
/* Finalizes a instance of the Data class. */
@ -559,3 +645,82 @@ Data::~Data()
{
delete[] m_pduUserData;
}
/* Helper to write a DMR PDU packet. */
void Data::writeRF_PDU(DataType::E dataType, const uint8_t* pdu)
{
assert(pdu != nullptr);
if ((dataType != DataType::DATA_HEADER) &&
(dataType != DataType::RATE_12_DATA) &&
(dataType != DataType::RATE_34_DATA) &&
(dataType != DataType::RATE_1_DATA))
return;
// don't add any frames if the queue is full
uint8_t len = DMR_FRAME_LENGTH_BYTES + 2U;
uint32_t space = m_slot->m_txQueue.freeSpace();
if (space < (len + 1U)) {
return;
}
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES);
::memcpy(data, pdu, DMR_FRAME_LENGTH_BYTES);
SlotType slotType;
slotType.setColorCode(m_slot->m_colorCode);
slotType.setDataType(dataType);
// 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] = modem::TAG_DATA;
data[1U] = 0x00U;
if (m_slot->m_duplex)
m_slot->addFrame(data);
}
/* Helper to write a PDU acknowledge response. */
void Data::writeRF_PDU_Ack_Response(uint8_t rspClass, uint8_t rspType, uint8_t rspStatus, uint8_t sap, bool gi, uint32_t srcId, uint32_t dstId)
{
if (rspClass == PDUResponseClass::ACK && rspType != PDUResponseType::ACK)
return;
edac::BPTC19696 bptc;
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES);
data::DataHeader rspHeader = data::DataHeader();
rspHeader.setDPF(DPF::RESPONSE);
rspHeader.setSAP(sap);
rspHeader.setGI(gi);
rspHeader.setSrcId(srcId);
rspHeader.setDstId(dstId);
rspHeader.setResponseClass(rspClass);
rspHeader.setResponseType(rspType);
rspHeader.setResponseStatus(rspStatus);
rspHeader.setBlocks(1U);
rspHeader.encode(data + 2U);
writeRF_PDU(DataType::DATA_HEADER, data);
::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);
// encode the BPTC (196,96) FEC
bptc.encode(payload, data + 2U);
writeRF_PDU(DataType::RATE_12_DATA, data);
}

@ -99,6 +99,25 @@ namespace dmr
* @brief Finalizes a instance of the Data class.
*/
~Data();
/**
* @brief Helper to write a DMR PDU packet.
* @param dataType DMR Data Type for this frame.
* @param[in] pdu Buffer containing encoded PDU to transmit.
*/
void writeRF_PDU(defines::DataType::E dataType, const uint8_t* pdu);
/**
* @brief Helper to write a PDU acknowledge response.
* @param rspClass Response Class.
* @param rspType Response Type.
* @param rspStatus Response Status.
* @param sap Service access point.
* @param gi Flag indicating group or individual.
* @param srcId Source ID.
* @param dstId Destination ID.
*/
void writeRF_PDU_Ack_Response(uint8_t rspClass, uint8_t rspType, uint8_t sap, uint8_t rspStatus, bool gi, uint32_t srcId, uint32_t dstId);
};
} // namespace packet
} // namespace dmr

@ -204,7 +204,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
m_slot->m_rfLastSrcId = srcId;
if (m_slot->m_netState == RS_NET_IDLE) {
m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true);
m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, Slot::SLCO_ACT_TYPE::VOICE);
}
if (m_verbose) {
@ -618,7 +618,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
m_slot->m_rfLastSrcId = srcId;
if (m_slot->m_netState == RS_NET_IDLE) {
m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true);
m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, Slot::SLCO_ACT_TYPE::VOICE);
}
::ActivityLog("DMR", true, "Slot %u RF late entry from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO::GROUP ? "TG " : "", dstId);
@ -728,7 +728,7 @@ void Voice::processNetwork(const data::Data& dmrData)
m_slot->m_netLastSrcId = srcId;
m_slot->m_netTGHang.start();
m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true);
m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, Slot::SLCO_ACT_TYPE::VOICE);
if (m_verbose) {
LogMessage(LOG_NET, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo,
@ -797,7 +797,7 @@ void Voice::processNetwork(const data::Data& dmrData)
m_slot->m_netLastSrcId = srcId;
m_slot->m_netTGHang.start();
m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_netLC->getFLCO(), true);
m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_netLC->getFLCO(), Slot::SLCO_ACT_TYPE::VOICE);
::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);
@ -903,7 +903,7 @@ void Voice::processNetwork(const data::Data& dmrData)
m_slot->m_netLastSrcId = srcId;
m_slot->m_netTGHang.start();
m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_netLC->getFLCO(), true);
m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_netLC->getFLCO(), Slot::SLCO_ACT_TYPE::VOICE);
::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);

@ -417,16 +417,14 @@ bool Data::process(uint8_t* data, uint32_t len)
}
}
}
/*
if (m_repeatPDU) {
if (!m_rfDataHeader.getFullMessage()) {
m_rfDataHeader.setSAP(PDUSAP::EXT_ADDR);
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u, srcLlId = %u", m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId());
}
writeRF_PDU_Ack_Response(m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(),
m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId());
}
*/
writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on
}
}
else {
// handle standard P25 service access points
@ -1524,73 +1522,75 @@ void Data::writeRF_PDU_Buffered()
Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
offset += P25_PDU_FEC_LENGTH_BITS;
uint32_t dataOffset = 0U;
if (blocksToFollow > 0U) {
uint32_t dataOffset = 0U;
// generate the second PDU header
if (m_rfUseSecondHeader) {
m_rfSecondHeader.encode(m_pduUserData, true);
// generate the second PDU header
if (m_rfUseSecondHeader && m_rfDataHeader.getFormat() != PDUFormatType::RSP) {
m_rfSecondHeader.encode(m_pduUserData, true);
::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
m_rfSecondHeader.encode(block);
Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
m_rfSecondHeader.encode(block);
Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
bitLength += P25_PDU_FEC_LENGTH_BITS;
offset += P25_PDU_FEC_LENGTH_BITS;
dataOffset += P25_PDU_HEADER_LENGTH_BYTES;
blocksToFollow--;
bitLength += P25_PDU_FEC_LENGTH_BITS;
offset += P25_PDU_FEC_LENGTH_BITS;
dataOffset += P25_PDU_HEADER_LENGTH_BYTES;
blocksToFollow--;
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", OSP, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u",
m_rfSecondHeader.getFormat(), m_rfSecondHeader.getMFId(), m_rfSecondHeader.getSAP(), m_rfSecondHeader.getFullMessage(),
m_rfSecondHeader.getBlocksToFollow(), m_rfSecondHeader.getPadLength(), m_rfSecondHeader.getNs(), m_rfSecondHeader.getFSN(), m_rfSecondHeader.getLastFragment(),
m_rfSecondHeader.getHeaderOffset(), bitLength, m_rfSecondHeader.getLLId());
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", OSP, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u",
m_rfSecondHeader.getFormat(), m_rfSecondHeader.getMFId(), m_rfSecondHeader.getSAP(), m_rfSecondHeader.getFullMessage(),
m_rfSecondHeader.getBlocksToFollow(), m_rfSecondHeader.getPadLength(), m_rfSecondHeader.getNs(), m_rfSecondHeader.getFSN(), m_rfSecondHeader.getLastFragment(),
m_rfSecondHeader.getHeaderOffset(), bitLength, m_rfSecondHeader.getLLId());
}
}
}
edac::CRC::addCRC32(m_pduUserData, m_pduUserDataLength);
edac::CRC::addCRC32(m_pduUserData, m_pduUserDataLength);
// generate the PDU data
for (uint32_t i = 0U; i < blocksToFollow; i++) {
m_rfData[i].setFormat((m_rfUseSecondHeader) ? m_rfSecondHeader : m_rfDataHeader);
m_rfData[i].setSerialNo(i);
m_rfData[i].setData(m_pduUserData + dataOffset);
// generate the PDU data
for (uint32_t i = 0U; i < blocksToFollow; i++) {
m_rfData[i].setFormat((m_rfUseSecondHeader) ? m_rfSecondHeader : m_rfDataHeader);
m_rfData[i].setSerialNo(i);
m_rfData[i].setData(m_pduUserData + dataOffset);
// are we processing extended address data from the first block?
if (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR && m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED &&
m_rfData[i].getSerialNo() == 0U) {
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, llId = %u",
m_rfData[i].getSerialNo(), m_rfData[i].getFormat(), m_rfData[i].getLastBlock(), m_rfData[i].getSAP(), m_rfData[i].getLLId());
// are we processing extended address data from the first block?
if (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR && m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED &&
m_rfData[i].getSerialNo() == 0U) {
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, llId = %u",
m_rfData[i].getSerialNo(), m_rfData[i].getFormat(), m_rfData[i].getLastBlock(), m_rfData[i].getSAP(), m_rfData[i].getLLId());
if (m_dumpPDUData) {
uint8_t dataBlock[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES];
::memset(dataBlock, 0xAAU, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
m_rfData[i].getData(dataBlock);
Utils::dump(2U, "Data Block", dataBlock, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
if (m_dumpPDUData) {
uint8_t dataBlock[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES];
::memset(dataBlock, 0xAAU, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
m_rfData[i].getData(dataBlock);
Utils::dump(2U, "Data Block", dataBlock, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
}
}
}
}
else {
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u",
(m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_rfData[i].getSerialNo() : i, m_rfData[i].getFormat(),
m_rfData[i].getLastBlock());
else {
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u",
(m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_rfData[i].getSerialNo() : i, m_rfData[i].getFormat(),
m_rfData[i].getLastBlock());
if (m_dumpPDUData) {
uint8_t dataBlock[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES];
::memset(dataBlock, 0xAAU, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
m_rfData[i].getData(dataBlock);
Utils::dump(2U, "Data Block", dataBlock, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
if (m_dumpPDUData) {
uint8_t dataBlock[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES];
::memset(dataBlock, 0xAAU, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
m_rfData[i].getData(dataBlock);
Utils::dump(2U, "Data Block", dataBlock, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
}
}
}
}
::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
m_rfData[i].encode(block);
Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
m_rfData[i].encode(block);
Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
offset += P25_PDU_FEC_LENGTH_BITS;
dataOffset += (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES;
offset += P25_PDU_FEC_LENGTH_BITS;
dataOffset += (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES;
}
}
writeRF_PDU(data, bitLength);

Loading…
Cancel
Save

Powered by TurnKey Linux.