You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dvmhost/src/host/dmr/packet/Data.cpp

727 lines
29 KiB

// 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 <cassert>
#include <algorithm>
// ---------------------------------------------------------------------------
// 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<data::DataHeader>(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<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);
// 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<data::DataHeader>(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<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);
// 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);
}

Powered by TurnKey Linux.