diff --git a/src/common/dmr/DMRDefines.h b/src/common/dmr/DMRDefines.h index b579a3fd..a19dccef 100644 --- a/src/common/dmr/DMRDefines.h +++ b/src/common/dmr/DMRDefines.h @@ -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 /** @} */ diff --git a/src/common/dmr/data/DataHeader.cpp b/src/common/dmr/data/DataHeader.cpp index 537bc6b9..669ddcb4 100644 --- a/src/common/dmr/data/DataHeader.cpp +++ b/src/common/dmr/data/DataHeader.cpp @@ -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) diff --git a/src/common/dmr/data/DataHeader.h b/src/common/dmr/data/DataHeader.h index 57a8af40..c193310a 100644 --- a/src/common/dmr/data/DataHeader.h +++ b/src/common/dmr/data/DataHeader.h @@ -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; diff --git a/src/common/edac/Trellis.cpp b/src/common/edac/Trellis.cpp index ca26b83a..2ab37994 100644 --- a/src/common/edac/Trellis.cpp +++ b/src/common/edac/Trellis.cpp @@ -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); } } diff --git a/src/common/edac/Trellis.h b/src/common/edac/Trellis.h index 1d91f517..8bb9a7b4 100644 --- a/src/common/edac/Trellis.h +++ b/src/common/edac/Trellis.h @@ -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. diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 14cddea5..c75f8702 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -164,8 +164,11 @@ int Host::run() // initialize system logging yaml::Node logConf = m_conf["log"]; + bool useSyslog = logConf["useSyslog"].as(false); + if (g_foreground) + useSyslog = false; ret = ::LogInitialise(logConf["filePath"].as(), logConf["fileRoot"].as(), - logConf["fileLevel"].as(0U), logConf["displayLevel"].as(0U), false, logConf["useSyslog"].as(false)); + logConf["fileLevel"].as(0U), logConf["displayLevel"].as(0U), false, useSyslog); if (!ret) { ::fatal("unable to open the log file\n"); } diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 5819c9c3..3ce59b19 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -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); diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 4de554fb..b1a64618 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -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. diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index d38ec680..e9e46eb8 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -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: diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index e353d21d..1d79233e 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -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(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(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); +} diff --git a/src/host/dmr/packet/Data.h b/src/host/dmr/packet/Data.h index a53515dc..cbf7df87 100644 --- a/src/host/dmr/packet/Data.h +++ b/src/host/dmr/packet/Data.h @@ -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 diff --git a/src/host/dmr/packet/Voice.cpp b/src/host/dmr/packet/Voice.cpp index 81ec9106..930f7364 100644 --- a/src/host/dmr/packet/Voice.cpp +++ b/src/host/dmr/packet/Voice.cpp @@ -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); diff --git a/src/host/p25/packet/Data.cpp b/src/host/p25/packet/Data.cpp index c96f02c8..44e76b0d 100644 --- a/src/host/p25/packet/Data.cpp +++ b/src/host/p25/packet/Data.cpp @@ -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);