// SPDX-License-Identifier: GPL-2.0-only /* * Digital Voice Modem - Modem Host Software * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" #include "common/dmr/acl/AccessControl.h" #include "common/dmr/lc/FullLC.h" #include "common/dmr/lc/CSBK.h" #include "common/dmr/SlotType.h" #include "common/dmr/Sync.h" #include "common/edac/BPTC19696.h" #include "common/edac/Trellis.h" #include "common/Log.h" #include "common/Utils.h" #include "dmr/packet/Data.h" #include "dmr/Slot.h" #include "ActivityLog.h" using namespace dmr; using namespace dmr::defines; using namespace dmr::packet; #include #include // --------------------------------------------------------------------------- // Macros // --------------------------------------------------------------------------- #define CHECK_AUTHORITATIVE(_DST_ID) \ if (!m_slot->m_authoritative && m_slot->m_permittedDstId != dstId) { \ LogWarning(LOG_RF, "[NON-AUTHORITATIVE] Ignoring RF traffic, destination not permitted!"); \ m_slot->m_rfState = RS_RF_LISTENING; \ return false; \ } #define CHECK_NET_AUTHORITATIVE(_DST_ID) \ if (!m_slot->m_authoritative && m_slot->m_permittedDstId != dstId) { \ return; \ } // Don't process RF frames if the network isn't in a idle state. #define CHECK_TRAFFIC_COLLISION(_DST_ID) \ if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \ LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing network traffic!", m_slot->m_slotNo); \ m_slot->m_rfState = RS_RF_LISTENING; \ return false; \ } \ if (m_slot->m_enableTSCC && _DST_ID == m_slot->m_netLastDstId) { \ if (m_slot->m_affiliations->isNetGranted(_DST_ID)) { \ LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing granted network traffic (Are we in a voting condition?)", m_slot->m_slotNo); \ m_slot->m_rfState = RS_RF_LISTENING; \ return false; \ } \ } #define CHECK_TG_HANG(_DST_ID) \ if (m_slot->m_rfLastDstId != 0U) { \ if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \ return; \ } \ } // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /* Process DMR data frame from the RF interface. */ bool Data::process(uint8_t* data, uint32_t len) { assert(data != nullptr); // get the type from the packet metadata DataType::E dataType = (DataType::E)(data[1U] & 0x0FU); SlotType slotType; slotType.setColorCode(m_slot->m_colorCode); slotType.setDataType(dataType); if (dataType == DataType::TERMINATOR_WITH_LC) { if (m_slot->m_rfState != RS_RF_AUDIO) return false; // Regenerate the LC data lc::FullLC fullLC; fullLC.encode(*m_slot->m_rfLC, data + 2U, DataType::TERMINATOR_WITH_LC); // 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); if (!m_slot->m_rfTimeout) { data[0U] = modem::TAG_EOT; data[1U] = 0x00U; m_slot->writeNetwork(data, DataType::TERMINATOR_WITH_LC); if (m_slot->m_duplex) { for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) m_slot->addFrame(data); } } if (m_verbose) { LogMessage(LOG_RF, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot->m_slotNo, m_slot->m_rfLC->getDstId()); } // release trunked grant (if necessary) Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); if (m_tscc != nullptr) { if (m_tscc->m_enableTSCC) { m_tscc->m_affiliations->releaseGrant(m_slot->m_rfLC->getDstId(), false); m_slot->clearTSCCActivated(); } } if (m_slot->m_rssi != 0U) { ::ActivityLog("DMR", true, "Slot %u RF end of voice transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_slot->m_slotNo, float(m_slot->m_rfFrames) / 16.667F, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits), m_slot->m_minRSSI, m_slot->m_maxRSSI, m_slot->m_aveRSSI / m_slot->m_rssiCount); } else { ::ActivityLog("DMR", true, "Slot %u RF end of voice transmission, %.1f seconds, BER: %.1f%%", m_slot->m_slotNo, float(m_slot->m_rfFrames) / 16.667F, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits)); } LogMessage(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", m_slot->m_slotNo, m_slot->m_rfFrames, m_slot->m_rfBits, m_slot->m_rfErrs, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits)); m_slot->m_dmr->tsccClearActivatedSlot(m_slot->m_slotNo); if (m_slot->m_rfTimeout) { m_slot->writeEndRF(); return false; } else { m_slot->writeEndRF(); return true; } } else if (dataType == DataType::DATA_HEADER) { if (m_slot->m_rfState == RS_RF_DATA) return true; data::DataHeader* dataHeader = new data::DataHeader(); bool valid = dataHeader->decode(data + 2U); if (!valid) return false; m_slot->m_rfDataHeader = std::unique_ptr(dataHeader); bool gi = dataHeader->getGI(); uint32_t srcId = dataHeader->getSrcId(); uint32_t dstId = dataHeader->getDstId(); CHECK_AUTHORITATIVE(dstId); CHECK_TRAFFIC_COLLISION(dstId); if (m_slot->m_tsccPayloadDstId != 0U && m_slot->m_tsccPayloadActRetry.isRunning()) { m_slot->m_tsccPayloadActRetry.stop(); } // validate the source RID if (!acl::AccessControl::validateSrcId(srcId)) { LogWarning(LOG_RF, "DMR Slot %u, DATA_HEADER denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId); m_slot->m_rfState = RS_RF_REJECTED; return false; } // validate the target ID if (gi) { if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) { LogWarning(LOG_RF, "DMR Slot %u, DATA_HEADER denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; return false; } } m_slot->m_rfFrames = dataHeader->getBlocks(); 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); // Regenerate the Slot Type slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); data[0U] = m_slot->m_rfFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; data[1U] = 0x00U; if (m_slot->m_duplex && m_repeatDataPacket) m_slot->addFrame(data); m_slot->writeNetwork(data, DataType::DATA_HEADER); m_slot->m_rfState = RS_RF_DATA; m_slot->m_rfLastDstId = dstId; 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, 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 * PDU_UNCODED_LENGTH_BYTES + 2U); m_pduDataOffset = 0U; if (m_slot->m_rfFrames == 0U) { ::ActivityLog("DMR", true, "Slot %u ended RF data transmission", m_slot->m_slotNo); m_slot->writeEndRF(); } return true; } else if (dataType == DataType::RATE_12_DATA || dataType == DataType::RATE_34_DATA || dataType == DataType::RATE_1_DATA) { 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; // 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); if (ret) { // store payload ::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_CONFIRMED_LENGTH_BYTES); // encode the Trellis 3/4 rate FEC 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 += 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--; data[0U] = m_slot->m_rfFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; data[1U] = 0x00U; // 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->writeNetwork(data, dataType); if (m_slot->m_duplex && m_repeatDataPacket) { m_slot->addFrame(data); } 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; } return false; } /* Process a data frame from the network. */ void Data::processNetwork(const data::Data& dmrData) { uint8_t dataType = dmrData.getDataType(); uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; dmrData.getData(data + 2U); if (dataType == DataType::TERMINATOR_WITH_LC) { if (m_slot->m_netState != RS_NET_AUDIO) return; // Regenerate the LC data lc::FullLC fullLC; fullLC.encode(*m_slot->m_netLC, data + 2U, DataType::TERMINATOR_WITH_LC); // Regenerate the Slot Type SlotType slotType; slotType.setColorCode(m_slot->m_colorCode); slotType.setDataType(DataType::TERMINATOR_WITH_LC); 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); if (!m_slot->m_netTimeout) { data[0U] = modem::TAG_EOT; data[1U] = 0x00U; if (m_slot->m_duplex) { for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) m_slot->addFrame(data, true); } else { for (uint32_t i = 0U; i < 3U; i++) m_slot->addFrame(data, true); } } if (m_verbose) { LogMessage(LOG_RF, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot->m_slotNo, m_slot->m_netLC->getDstId()); } // release trunked grant (if necessary) Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); if (m_tscc != nullptr) { if (m_tscc->m_enableTSCC) { m_tscc->m_affiliations->releaseGrant(m_slot->m_netLC->getDstId(), false); m_slot->clearTSCCActivated(); } } // We've received the voice header and terminator haven't we? m_slot->m_netFrames += 2U; ::ActivityLog("DMR", false, "Slot %u network end of voice transmission, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_slot->m_slotNo, float(m_slot->m_netFrames) / 16.667F, (m_slot->m_netLost * 100U) / m_slot->m_netFrames, float(m_slot->m_netErrs * 100U) / float(m_slot->m_netBits)); m_slot->m_dmr->tsccClearActivatedSlot(m_slot->m_slotNo); m_slot->writeEndNet(); } else if (dataType == DataType::DATA_HEADER) { if (m_slot->m_netState == RS_NET_DATA) return; data::DataHeader* dataHeader = new data::DataHeader(); bool valid = dataHeader->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(dataHeader); bool gi = dataHeader->getGI(); uint32_t srcId = dataHeader->getSrcId(); uint32_t dstId = dataHeader->getDstId(); CHECK_NET_AUTHORITATIVE(dstId); CHECK_TG_HANG(dstId); if (m_slot->m_tsccPayloadDstId != 0U && m_slot->m_tsccPayloadActRetry.isRunning()) { m_slot->m_tsccPayloadActRetry.stop(); } 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); // Regenerate the Slot Type SlotType slotType; slotType.setColorCode(m_slot->m_colorCode); slotType.setDataType(DataType::DATA_HEADER); slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); data[0U] = m_slot->m_netFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; data[1U] = 0x00U; // Put a small delay into starting transmission m_slot->addFrame(m_slot->m_idle, true); m_slot->addFrame(m_slot->m_idle, true); m_slot->addFrame(data, true); m_slot->m_netState = RS_NET_DATA; m_slot->m_netLastDstId = dstId; m_slot->m_netLastSrcId = srcId; 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); } ::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); m_pduDataOffset = 0U; if (m_slot->m_netFrames == 0U) { ::ActivityLog("DMR", false, "Slot %u ended network data transmission", m_slot->m_slotNo); m_slot->writeEndNet(); } } else if (dataType == DataType::RATE_12_DATA || dataType == DataType::RATE_34_DATA || dataType == DataType::RATE_1_DATA) { if (m_slot->m_netState != RS_NET_DATA || m_slot->m_netFrames == 0U) { m_slot->writeEndNet(); 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; // 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); if (ret) { // store payload ::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_CONFIRMED_LENGTH_BYTES); // encode the Trellis 3/4 rate FEC 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 += 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--; if (m_repeatDataPacket) { // regenerate the Slot Type SlotType slotType; slotType.decode(data + 2U); slotType.setColorCode(m_slot->m_colorCode); slotType.encode(data + 2U); // convert the Data Sync to be from the BS or MS as needed Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); data[0U] = m_slot->m_netFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; 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(); } } else { // Unhandled data type LogWarning(LOG_NET, "DMR Slot %u, unhandled network data, type = $%02X", m_slot->m_slotNo, dataType); } } // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- /* Initializes a new instance of the Data class. */ Data::Data(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) : m_slot(slot), m_pduUserData(nullptr), m_pduDataOffset(0U), m_lastRejectId(0U), m_dumpDataPacket(dumpDataPacket), m_repeatDataPacket(repeatDataPacket), 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); } /* Finalizes a instance of the Data class. */ 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); }