remove standalone P25 DataRspHeader class and merge it into DataHeader; further enhance the P25 DataHeader to better match the TIA spec; clean up some code and variable references;

pull/12/head
Bryan Biedenkapp 4 years ago
parent 8207a35a84
commit e931d8d26e

@ -210,7 +210,6 @@
<ClInclude Include="p25\acl\AccessControl.h" /> <ClInclude Include="p25\acl\AccessControl.h" />
<ClInclude Include="p25\data\DataBlock.h" /> <ClInclude Include="p25\data\DataBlock.h" />
<ClInclude Include="p25\data\DataHeader.h" /> <ClInclude Include="p25\data\DataHeader.h" />
<ClInclude Include="p25\data\DataRspHeader.h" />
<ClInclude Include="p25\data\LowSpeedData.h" /> <ClInclude Include="p25\data\LowSpeedData.h" />
<ClInclude Include="p25\dfsi\DFSIDefines.h" /> <ClInclude Include="p25\dfsi\DFSIDefines.h" />
<ClInclude Include="p25\dfsi\LC.h" /> <ClInclude Include="p25\dfsi\LC.h" />
@ -290,7 +289,6 @@
<ClCompile Include="p25\acl\AccessControl.cpp" /> <ClCompile Include="p25\acl\AccessControl.cpp" />
<ClCompile Include="p25\data\DataBlock.cpp" /> <ClCompile Include="p25\data\DataBlock.cpp" />
<ClCompile Include="p25\data\DataHeader.cpp" /> <ClCompile Include="p25\data\DataHeader.cpp" />
<ClCompile Include="p25\data\DataRspHeader.cpp" />
<ClCompile Include="p25\data\LowSpeedData.cpp" /> <ClCompile Include="p25\data\LowSpeedData.cpp" />
<ClCompile Include="p25\edac\Trellis.cpp" /> <ClCompile Include="p25\edac\Trellis.cpp" />
<ClCompile Include="p25\lc\LC.cpp" /> <ClCompile Include="p25\lc\LC.cpp" />

@ -338,9 +338,6 @@
<ClInclude Include="network\BaseNetwork.h"> <ClInclude Include="network\BaseNetwork.h">
<Filter>Header Files\network</Filter> <Filter>Header Files\network</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="p25\data\DataRspHeader.h">
<Filter>Header Files\p25\data</Filter>
</ClInclude>
<ClInclude Include="dmr\SiteData.h"> <ClInclude Include="dmr\SiteData.h">
<Filter>Header Files\dmr</Filter> <Filter>Header Files\dmr</Filter>
</ClInclude> </ClInclude>
@ -568,9 +565,6 @@
<ClCompile Include="network\BaseNetwork.cpp"> <ClCompile Include="network\BaseNetwork.cpp">
<Filter>Source Files\network</Filter> <Filter>Source Files\network</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="p25\data\DataRspHeader.cpp">
<Filter>Source Files\p25\data</Filter>
</ClCompile>
<ClCompile Include="dmr\ControlPacket.cpp"> <ClCompile Include="dmr\ControlPacket.cpp">
<Filter>Source Files\dmr</Filter> <Filter>Source Files\dmr</Filter>
</ClCompile> </ClCompile>

@ -42,7 +42,6 @@ OBJECTS = \
p25/acl/AccessControl.o \ p25/acl/AccessControl.o \
p25/data/DataBlock.o \ p25/data/DataBlock.o \
p25/data/DataHeader.o \ p25/data/DataHeader.o \
p25/data/DataRspHeader.o \
p25/data/LowSpeedData.o \ p25/data/LowSpeedData.o \
p25/dfsi/LC.o \ p25/dfsi/LC.o \
p25/edac/Trellis.o \ p25/edac/Trellis.o \

@ -41,7 +41,6 @@ OBJECTS = \
p25/acl/AccessControl.o \ p25/acl/AccessControl.o \
p25/data/DataBlock.o \ p25/data/DataBlock.o \
p25/data/DataHeader.o \ p25/data/DataHeader.o \
p25/data/DataRspHeader.o \
p25/data/LowSpeedData.o \ p25/data/LowSpeedData.o \
p25/dfsi/LC.o \ p25/dfsi/LC.o \
p25/edac/Trellis.o \ p25/edac/Trellis.o \

@ -42,7 +42,6 @@ OBJECTS = \
p25/acl/AccessControl.o \ p25/acl/AccessControl.o \
p25/data/DataBlock.o \ p25/data/DataBlock.o \
p25/data/DataHeader.o \ p25/data/DataHeader.o \
p25/data/DataRspHeader.o \
p25/data/LowSpeedData.o \ p25/data/LowSpeedData.o \
p25/dfsi/LC.o \ p25/dfsi/LC.o \
p25/edac/Trellis.o \ p25/edac/Trellis.o \

@ -449,15 +449,15 @@ uint8_t CRC::crc8(const uint8_t *in, uint32_t length)
/// Generate 9-bit CRC. /// Generate 9-bit CRC.
/// </summary> /// </summary>
/// <param name="in">Input byte array.</param> /// <param name="in">Input byte array.</param>
/// <param name="bitlen">Length of byte array in bits.</param> /// <param name="bitLength">Length of byte array in bits.</param>
/// <returns>Calculated 9-bit CRC value.</returns> /// <returns>Calculated 9-bit CRC value.</returns>
uint16_t CRC::crc9(const uint8_t* in, uint32_t bitlen) uint16_t CRC::crc9(const uint8_t* in, uint32_t bitLength)
{ {
assert(in != NULL); assert(in != NULL);
uint16_t crc = 0x00U; uint16_t crc = 0x00U;
for (uint32_t i = 0; i < bitlen; i++) { for (uint32_t i = 0; i < bitLength; i++) {
bool b = READ_BIT(in, i); bool b = READ_BIT(in, i);
if (b) { if (b) {
if (i < 7U) { if (i < 7U) {
@ -473,7 +473,7 @@ uint16_t CRC::crc9(const uint8_t* in, uint32_t bitlen)
crc ^= 0x1FFU; crc ^= 0x1FFU;
#if DEBUG_CRC #if DEBUG_CRC
LogDebug(LOG_HOST, "CRC:crc9(), crc = $%03X, bitlen = %u", crc, bitlen); LogDebug(LOG_HOST, "CRC:crc9(), crc = $%03X, bitlen = %u", crc, bitLength);
#endif #endif
return crc; return crc;

@ -66,7 +66,7 @@ namespace edac
static uint8_t crc8(const uint8_t* in, uint32_t length); static uint8_t crc8(const uint8_t* in, uint32_t length);
/// <summary>Generate 9-bit CRC.</summary> /// <summary>Generate 9-bit CRC.</summary>
static uint16_t crc9(const uint8_t* in, uint32_t bitlen); static uint16_t crc9(const uint8_t* in, uint32_t bitLength);
}; };
} // namespace edac } // namespace edac

@ -13,7 +13,7 @@
/* /*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2017,2018 by Andy Uribe CA6JAU * Copyright (C) 2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2017-2021 by Bryan Biedenkapp N2PLL * Copyright (C) 2017-2022 by Bryan Biedenkapp N2PLL
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -1741,9 +1741,10 @@ void HostCal::processP25BER(const uint8_t* buffer)
Utils::dump(1U, "Unfixable PDU Data", rfPDU + P25_SYNC_LENGTH_BYTES + P25_NID_LENGTH_BYTES, P25_PDU_HEADER_LENGTH_BYTES); Utils::dump(1U, "Unfixable PDU Data", rfPDU + P25_SYNC_LENGTH_BYTES + P25_NID_LENGTH_BYTES, P25_PDU_HEADER_LENGTH_BYTES);
} }
else { else {
LogMessage(LOG_CAL, P25_PDU_STR ", fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, n = %u, seqNo = %u", LogMessage(LOG_CAL, P25_PDU_STR ", ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u",
dataHeader.getFormat(), dataHeader.getSAP(), dataHeader.getFullMessage(), dataHeader.getBlocksToFollow(), dataHeader.getPadCount(), dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(),
dataHeader.getN(), dataHeader.getSeqNo()); dataHeader.getBlocksToFollow(), dataHeader.getPadCount(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(),
dataHeader.getHeaderOffset());
} }
delete[] rfPDU; delete[] rfPDU;

@ -96,7 +96,9 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
m_rfDataBlockCnt = 0U; m_rfDataBlockCnt = 0U;
m_rfPDUCount = 0U; m_rfPDUCount = 0U;
m_rfPDUBits = 0U; m_rfPDUBits = 0U;
::memset(m_rfPDU, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U);
m_p25->m_rfState = RS_RF_DATA; m_p25->m_rfState = RS_RF_DATA;
::memset(m_pduUserData, 0x00U, P25_MAX_PDU_COUNT * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); ::memset(m_pduUserData, 0x00U, P25_MAX_PDU_COUNT * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U);
@ -131,9 +133,9 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
} }
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, n = %u, seqNo = %u, hdrOffset = %u", LogMessage(LOG_RF, P25_PDU_STR ", ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u",
m_rfDataHeader.getAckNeeded(), m_rfDataHeader.getOutbound(), m_rfDataHeader.getFormat(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMessage(), m_rfDataHeader.getAckNeeded(), m_rfDataHeader.getOutbound(), m_rfDataHeader.getFormat(), m_rfDataHeader.getMFId(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMessage(),
m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadCount(), m_rfDataHeader.getN(), m_rfDataHeader.getSeqNo(), m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadCount(), m_rfDataHeader.getNs(), m_rfDataHeader.getFSN(), m_rfDataHeader.getLastFragment(),
m_rfDataHeader.getHeaderOffset()); m_rfDataHeader.getHeaderOffset());
} }
@ -175,9 +177,9 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
} }
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", LogMessage(LOG_RF, P25_PDU_STR ", fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u",
m_rfSecondHeader.getFormat(), m_rfSecondHeader.getSAP(), m_rfSecondHeader.getFullMessage(), m_rfSecondHeader.getFormat(), m_rfSecondHeader.getMFId(), m_rfSecondHeader.getSAP(), m_rfSecondHeader.getFullMessage(),
m_rfSecondHeader.getBlocksToFollow(), m_rfSecondHeader.getPadCount(), m_rfSecondHeader.getN(), m_rfSecondHeader.getSeqNo(), m_rfSecondHeader.getBlocksToFollow(), m_rfSecondHeader.getPadCount(), m_rfSecondHeader.getNs(), m_rfSecondHeader.getFSN(), m_rfSecondHeader.getLastFragment(),
m_rfSecondHeader.getHeaderOffset(), m_rfSecondHeader.getLLId()); m_rfSecondHeader.getHeaderOffset(), m_rfSecondHeader.getLLId());
} }
@ -334,7 +336,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
LogMessage(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfUseSecondHeader) ? m_rfSecondHeader.getLLId() : m_rfDataHeader.getLLId()); LogMessage(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfUseSecondHeader) ? m_rfSecondHeader.getLLId() : m_rfDataHeader.getLLId());
} }
writeRF_PDU(); // re-generate PDU and send it on writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on
} }
::ActivityLog("P25", true, "end of RF data transmission"); ::ActivityLog("P25", true, "end of RF data transmission");
@ -607,29 +609,76 @@ void DataPacket::writeNetworkRF(const uint8_t dataType, const uint8_t *data, uin
/// <summary> /// <summary>
/// Helper to write a P25 PDU packet. /// Helper to write a P25 PDU packet.
/// </summary> /// </summary>
void DataPacket::writeRF_PDU() /// <param name="pdu"></param>
/// <param name="bitlength"></param>
/// <param name="noNulls"></param>
/// <remarks>This simply takes data packed into m_rfPDU and transmits it.</remarks>
void DataPacket::writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool noNulls)
{
assert(pdu != NULL);
assert(bitLength > 0U);
uint8_t data[P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U];
::memset(data, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U);
if (m_debug) {
Utils::dump(2U, "!!! *Raw PDU Frame Data - P25_DUID_PDU", pdu, bitLength / 8U);
}
// Add the data
uint32_t newBitLength = P25Utils::encode(pdu, data + 2U, bitLength);
uint32_t newByteLength = newBitLength / 8U;
if ((newBitLength % 8U) > 0U)
newByteLength++;
// Regenerate Sync
Sync::addP25Sync(data + 2U);
// Regenerate NID
m_p25->m_nid.encode(data + 2U, P25_DUID_PDU);
// Add busy bits
m_p25->addBusyBits(data + 2U, newBitLength, false, true);
if (m_p25->m_duplex) {
data[0U] = TAG_DATA;
data[1U] = 0x00U;
m_p25->writeQueueRF(data, newByteLength + 2U);
}
// add trailing null pad; only if control data isn't being transmitted
if (!m_p25->m_ccRunning && !noNulls) {
m_p25->writeRF_Nulls();
}
}
/// <summary>
/// Helper to re-write a received P25 PDU packet.
/// </summary>
/// <remarks>This will take buffered received PDU data and repeat it over the air.</remarks>
void DataPacket::writeRF_PDU_Buffered()
{ {
uint32_t bitLength = ((m_rfDataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; uint32_t bitLength = ((m_rfDataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS;
uint32_t offset = P25_PREAMBLE_LENGTH_BITS; uint32_t offset = P25_PREAMBLE_LENGTH_BITS;
::memset(m_rfPDU, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U); uint8_t data[bitLength / 8U];
::memset(data, 0x00U, bitLength / 8U);
uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; uint8_t block[P25_PDU_FEC_LENGTH_BYTES];
::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
uint32_t blocksToFollow = m_rfDataHeader.getBlocksToFollow(); uint32_t blocksToFollow = m_rfDataHeader.getBlocksToFollow();
// Generate the PDU header and 1/2 rate Trellis // Generate the PDU header and 1/2 rate Trellis
m_rfDataHeader.encode(buffer); m_rfDataHeader.encode(block);
Utils::setBitRange(buffer, m_rfPDU, offset, P25_PDU_FEC_LENGTH_BITS); Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
offset += P25_PDU_FEC_LENGTH_BITS; offset += P25_PDU_FEC_LENGTH_BITS;
// Generate the second PDU header // Generate the second PDU header
if (m_rfUseSecondHeader) { if (m_rfUseSecondHeader) {
::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
m_rfSecondHeader.encode(buffer); m_rfSecondHeader.encode(block);
Utils::setBitRange(buffer, m_rfPDU, offset, P25_PDU_FEC_LENGTH_BITS); Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
offset += P25_PDU_FEC_LENGTH_BITS; offset += P25_PDU_FEC_LENGTH_BITS;
blocksToFollow--; blocksToFollow--;
@ -642,47 +691,15 @@ void DataPacket::writeRF_PDU()
m_rfData[i].setSerialNo(i); m_rfData[i].setSerialNo(i);
m_rfData[i].setData(m_pduUserData + dataOffset); m_rfData[i].setData(m_pduUserData + dataOffset);
::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
m_rfData[i].encode(buffer); m_rfData[i].encode(block);
Utils::setBitRange(buffer, m_rfPDU, offset, P25_PDU_FEC_LENGTH_BITS); Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
offset += P25_PDU_FEC_LENGTH_BITS; offset += P25_PDU_FEC_LENGTH_BITS;
dataOffset += (m_rfDataHeader.getFormat() == PDU_FMT_CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; dataOffset += (m_rfDataHeader.getFormat() == PDU_FMT_CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES;
} }
if (m_debug) { writeRF_PDU(data, bitLength);
Utils::dump(2U, "!!! *Raw PDU Frame Data - P25_DUID_PDU", m_rfPDU, bitLength / 8U);
}
uint8_t pdu[P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U];
// Add the data
uint32_t newBitLength = P25Utils::encode(m_rfPDU, pdu + 2U, bitLength);
uint32_t newByteLength = newBitLength / 8U;
if ((newBitLength % 8U) > 0U)
newByteLength++;
// Regenerate Sync
Sync::addP25Sync(pdu + 2U);
// Regenerate NID
m_p25->m_nid.encode(pdu + 2U, P25_DUID_PDU);
// Add busy bits
m_p25->addBusyBits(pdu + 2U, newBitLength, false, true);
::memset(m_rfPDU, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U);
if (m_p25->m_duplex) {
pdu[0U] = TAG_DATA;
pdu[1U] = 0x00U;
m_p25->writeQueueRF(pdu, newByteLength + 2U);
}
// add trailing null pad; only if control data isn't being transmitted
if (!m_p25->m_ccRunning) {
m_p25->writeRF_Nulls();
}
} }
/// <summary> /// <summary>
@ -696,85 +713,62 @@ void DataPacket::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong6
if ((regType != PDU_REG_TYPE_RSP_ACCPT) && (regType != PDU_REG_TYPE_RSP_DENY)) if ((regType != PDU_REG_TYPE_RSP_ACCPT) && (regType != PDU_REG_TYPE_RSP_DENY))
return; return;
uint32_t bitLength = (2U * P25_PDU_FEC_LENGTH_BITS); // + P25_PREAMBLE_LENGTH_BITS; uint32_t bitLength = (2U * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS;
uint32_t offset = P25_PREAMBLE_LENGTH_BITS; uint32_t offset = P25_PREAMBLE_LENGTH_BITS;
uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; uint8_t data[bitLength / 8U];
::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); ::memset(data, 0x00U, bitLength / 8U);
uint8_t block[P25_PDU_FEC_LENGTH_BYTES];
::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
DataRspHeader rspHeader = DataRspHeader(); DataHeader rspHeader = DataHeader();
rspHeader.setFormat(PDU_FMT_RSP);
rspHeader.setOutbound(true); rspHeader.setOutbound(true);
rspHeader.setClass(PDU_ACK_CLASS_ACK); rspHeader.setClass(PDU_ACK_CLASS_ACK);
rspHeader.setType(PDU_ACK_TYPE_ACK); rspHeader.setType(PDU_ACK_TYPE_ACK);
rspHeader.setStatus(m_rfDataHeader.getNs());
rspHeader.setLLId(llId); rspHeader.setLLId(llId);
if (m_rfDataHeader.getSAP() == PDU_SAP_EXT_ADDR) { if (m_rfDataHeader.getSAP() == PDU_SAP_EXT_ADDR) {
rspHeader.setSrcLLId(P25_WUID_FNE); rspHeader.setSrcLLId(P25_WUID_FNE);
rspHeader.setExtended(true); rspHeader.setFullMessage(true);
} }
else { else {
rspHeader.setExtended(false); rspHeader.setFullMessage(false);
} }
rspHeader.setBlocksToFollow(1U); rspHeader.setBlocksToFollow(1U);
// Generate the PDU header and 1/2 rate Trellis // Generate the PDU header and 1/2 rate Trellis
rspHeader.encode(buffer); rspHeader.encode(block);
Utils::setBitRange(buffer, m_rfPDU, offset, P25_PDU_FEC_LENGTH_BITS); Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
offset += P25_PDU_FEC_LENGTH_BITS; offset += P25_PDU_FEC_LENGTH_BITS;
// build registration response data // build registration response data
::memset(buffer, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); ::memset(block, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
buffer[0U] = (regType << 4); // Registration Type & Options block[0U] = (regType << 4); // Registration Type & Options
buffer[1U] = (llId >> 16) & 0xFFU; // Logical Link ID block[1U] = (llId >> 16) & 0xFFU; // Logical Link ID
buffer[2U] = (llId >> 8) & 0xFFU; block[2U] = (llId >> 8) & 0xFFU;
buffer[3U] = (llId >> 0) & 0xFFU; block[3U] = (llId >> 0) & 0xFFU;
if (regType == PDU_REG_TYPE_RSP_ACCPT) { if (regType == PDU_REG_TYPE_RSP_ACCPT) {
buffer[8U] = (ipAddr >> 24) & 0xFFU; // IP Address block[8U] = (ipAddr >> 24) & 0xFFU; // IP Address
buffer[9U] = (ipAddr >> 16) & 0xFFU; block[9U] = (ipAddr >> 16) & 0xFFU;
buffer[10U] = (ipAddr >> 8) & 0xFFU; block[10U] = (ipAddr >> 8) & 0xFFU;
buffer[11U] = (ipAddr >> 0) & 0xFFU; block[11U] = (ipAddr >> 0) & 0xFFU;
} }
edac::CRC::addCRC32(buffer, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); edac::CRC::addCRC32(block, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES);
// Generate the PDU data // Generate the PDU data
m_rfData[0].setFormat(PDU_FMT_RSP); m_rfData[0].setFormat(PDU_FMT_RSP);
m_rfData[0].setConfirmed(true); m_rfData[0].setConfirmed(true);
m_rfData[0].setSerialNo(0U); m_rfData[0].setSerialNo(0U);
m_rfData[0].setData(buffer); m_rfData[0].setData(block);
::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
m_rfData[0].encode(buffer); m_rfData[0].encode(block);
Utils::setBitRange(buffer, m_rfPDU, offset, P25_PDU_FEC_LENGTH_BITS); Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
if (m_debug) { writeRF_PDU(data, bitLength);
Utils::dump(2U, "!!! *Raw PDU Frame Data - P25_DUID_PDU", m_rfPDU, bitLength / 8U);
}
uint8_t pdu[P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U];
// Regenerate Sync
Sync::addP25Sync(pdu + 2U);
// Regenerate NID
m_p25->m_nid.encode(pdu + 2U, P25_DUID_PDU);
// Add the data
uint32_t newBitLength = P25Utils::encode(m_rfPDU, pdu + 2U, 114U, bitLength);
uint32_t newByteLength = newBitLength / 8U;
if ((newBitLength % 8U) > 0U)
newByteLength++;
// Add busy bits
m_p25->addBusyBits(pdu + 2U, newBitLength, false, true);
::memset(m_rfPDU, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U);
if (m_p25->m_duplex) {
pdu[0U] = TAG_DATA;
pdu[1U] = 0x00U;
m_p25->writeQueueRF(pdu, newByteLength + 2U);
}
} }
/// <summary> /// <summary>
@ -788,52 +782,35 @@ void DataPacket::writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uin
if (ackClass == PDU_ACK_CLASS_ACK && ackType != PDU_ACK_TYPE_ACK) if (ackClass == PDU_ACK_CLASS_ACK && ackType != PDU_ACK_TYPE_ACK)
return; return;
uint32_t bitLength = (2U * P25_PDU_FEC_LENGTH_BITS); // + P25_PREAMBLE_LENGTH_BITS; uint32_t bitLength = (2U * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS;
uint32_t offset = P25_PREAMBLE_LENGTH_BITS; uint32_t offset = P25_PREAMBLE_LENGTH_BITS;
uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; uint8_t data[bitLength / 8U];
::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); ::memset(data, 0x00U, bitLength / 8U);
uint8_t block[P25_PDU_FEC_LENGTH_BYTES];
::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
DataRspHeader rspHeader = DataRspHeader(); DataHeader rspHeader = DataHeader();
rspHeader.setFormat(PDU_FMT_RSP);
rspHeader.setOutbound(true); rspHeader.setOutbound(true);
rspHeader.setClass(ackClass); rspHeader.setClass(ackClass);
rspHeader.setType(ackType); rspHeader.setType(ackType);
rspHeader.setStatus(m_rfDataHeader.getNs());
rspHeader.setLLId(llId); rspHeader.setLLId(llId);
rspHeader.setSrcLLId(P25_WUID_FNE); if (m_rfDataHeader.getSAP() == PDU_SAP_EXT_ADDR) {
rspHeader.setSrcLLId(P25_WUID_FNE);
rspHeader.setFullMessage(true);
}
else {
rspHeader.setFullMessage(false);
}
rspHeader.setBlocksToFollow(0U); rspHeader.setBlocksToFollow(0U);
// Generate the PDU header and 1/2 rate Trellis // Generate the PDU header and 1/2 rate Trellis
rspHeader.encode(buffer); rspHeader.encode(block);
Utils::setBitRange(buffer, m_rfPDU, offset, P25_PDU_FEC_LENGTH_BITS); Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
if (m_debug) {
Utils::dump(2U, "!!! *Raw PDU Frame Data - P25_DUID_PDU", m_rfPDU, bitLength / 8U);
}
uint8_t pdu[P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U];
// Regenerate Sync
Sync::addP25Sync(pdu + 2U);
// Regenerate NID
m_p25->m_nid.encode(pdu + 2U, P25_DUID_PDU);
// Add the data
uint32_t newBitLength = P25Utils::encode(m_rfPDU, pdu + 2U, 114U, bitLength);
uint32_t newByteLength = newBitLength / 8U;
if ((newBitLength % 8U) > 0U)
newByteLength++;
// Add busy bits writeRF_PDU(data, bitLength);
m_p25->addBusyBits(pdu + 2U, newBitLength, false, true);
::memset(m_rfPDU, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U);
if (m_p25->m_duplex) {
pdu[0U] = TAG_DATA;
pdu[1U] = 0x00U;
m_p25->writeQueueRF(pdu, newByteLength + 2U);
}
} }
/// <summary> /// <summary>
@ -875,7 +852,7 @@ void DataPacket::writeNet_PDU_Header()
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NET, P25_PDU_STR ", ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, n = %u, seqNo = %u, hdrOffset = %u", LogMessage(LOG_NET, P25_PDU_STR ", ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, n = %u, seqNo = %u, hdrOffset = %u",
m_netDataHeader.getAckNeeded(), m_netDataHeader.getOutbound(), m_netDataHeader.getFormat(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMessage(), m_netDataHeader.getAckNeeded(), m_netDataHeader.getOutbound(), m_netDataHeader.getFormat(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMessage(),
m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadCount(), m_netDataHeader.getN(), m_netDataHeader.getSeqNo(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadCount(), m_netDataHeader.getNs(), m_netDataHeader.getFSN(),
m_netDataHeader.getHeaderOffset()); m_netDataHeader.getHeaderOffset());
} }

@ -34,7 +34,6 @@
#include "Defines.h" #include "Defines.h"
#include "p25/data/DataBlock.h" #include "p25/data/DataBlock.h"
#include "p25/data/DataHeader.h" #include "p25/data/DataHeader.h"
#include "p25/data/DataRspHeader.h"
#include "p25/data/LowSpeedData.h" #include "p25/data/LowSpeedData.h"
#include "p25/lc/LC.h" #include "p25/lc/LC.h"
#include "p25/Control.h" #include "p25/Control.h"
@ -115,7 +114,9 @@ namespace p25
void writeNetworkRF(const uint8_t dataType, const uint8_t* data, uint32_t len); void writeNetworkRF(const uint8_t dataType, const uint8_t* data, uint32_t len);
/// <summary>Helper to write a P25 PDU packet.</summary> /// <summary>Helper to write a P25 PDU packet.</summary>
void writeRF_PDU(); void writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool noNulls = false);
/// <summary>Helper to re-write a received P25 PDU packet.</summary>
void writeRF_PDU_Buffered();
/// <summary>Helper to write a PDU registration response.</summary> /// <summary>Helper to write a PDU registration response.</summary>
void writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong64_t ipAddr); void writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong64_t ipAddr);
/// <summary>Helper to write a PDU acknowledge response.</summary> /// <summary>Helper to write a PDU acknowledge response.</summary>

@ -74,6 +74,7 @@ namespace p25
/// <summary>Flag indicating this is the last block in a sequence of block.</summary> /// <summary>Flag indicating this is the last block in a sequence of block.</summary>
__PROPERTY(bool, lastBlock, LastBlock); __PROPERTY(bool, lastBlock, LastBlock);
/// <summary>Logical link ID.</summary> /// <summary>Logical link ID.</summary>
__PROPERTY(uint32_t, llId, LLId); __PROPERTY(uint32_t, llId, LLId);
/// <summary>Service access point.</summary> /// <summary>Service access point.</summary>

@ -49,12 +49,19 @@ DataHeader::DataHeader() :
m_sap(0U), m_sap(0U),
m_mfId(P25_MFG_STANDARD), m_mfId(P25_MFG_STANDARD),
m_llId(0U), m_llId(0U),
m_fullMessage(false), m_F(true),
m_sync(false), m_S(false),
m_n(0U), m_fsn(0U),
m_seqNo(0U), m_Ns(0U),
m_lastFragment(true),
m_headerOffset(0U), m_headerOffset(0U),
m_srcLlId(0U),
m_rspClass(PDU_ACK_CLASS_NACK),
m_rspType(PDU_ACK_TYPE_NACK_ILLEGAL),
m_rspStatus(0U),
m_ambtOpcode(0U), m_ambtOpcode(0U),
m_ambtField8(0U),
m_ambtField9(0U),
m_trellis(), m_trellis(),
m_blocksToFollow(0U), m_blocksToFollow(0U),
m_padCount(0U), m_padCount(0U),
@ -97,23 +104,20 @@ bool DataHeader::decode(const uint8_t* data)
m_ackNeeded = (header[0U] & 0x40U) == 0x40U; // Acknowledge Needed m_ackNeeded = (header[0U] & 0x40U) == 0x40U; // Acknowledge Needed
m_outbound = (header[0U] & 0x20U) == 0x20U; // Inbound/Outbound m_outbound = (header[0U] & 0x20U) == 0x20U; // Inbound/Outbound
m_fmt = header[0U] & 0x1F; // Packet Format m_fmt = header[0U] & 0x1F; // Packet Format
m_sap = header[1U] & 0x3FU; // Service Access Point m_sap = header[1U] & 0x3FU; // Service Access Point
m_mfId = header[2U]; // Mfg Id. m_mfId = header[2U]; // Mfg Id.
m_llId = (header[3U] << 16) + (header[4U] << 8) + header[5U]; // Logical Link ID m_llId = (header[3U] << 16) + (header[4U] << 8) + header[5U]; // Logical Link ID
m_fullMessage = (header[6U] & 0x80U) == 0x80U; // Full Message Flag m_F = (header[6U] & 0x80U) == 0x80U; // Full Message Flag
m_blocksToFollow = header[6U] & 0x7FU; // Block Frames to Follow m_blocksToFollow = header[6U] & 0x7FU; // Block Frames to Follow
if (m_fmt == PDU_FMT_AMBT) {
m_padCount = header[7U] & 0x1FU; // Pad Count
if (m_fmt == PDU_FMT_RSP || m_fmt == PDU_FMT_AMBT) {
m_padCount = 0; m_padCount = 0;
m_ambtOpcode = header[7U] & 0x3FU; // AMBT Opcode
}
else {
m_padCount = header[7U] & 0x1FU; // Pad Count
} }
if (m_fmt == PDU_FMT_CONFIRMED) { if (m_fmt == PDU_FMT_CONFIRMED) {
@ -123,23 +127,40 @@ bool DataHeader::decode(const uint8_t* data)
m_dataOctets = 12 * m_blocksToFollow - 4 - m_padCount; m_dataOctets = 12 * m_blocksToFollow - 4 - m_padCount;
} }
if (m_fmt == PDU_FMT_AMBT) { switch (m_fmt) {
m_sync = false; case PDU_FMT_CONFIRMED:
m_S = (header[8U] & 0x80U) == 0x80U; // Re-synchronize Flag
m_n = 0U; m_Ns = (header[8U] >> 4) & 0x07U; // Packet Sequence No.
m_seqNo = 0U; m_fsn = header[8U] & 0x07U; // Fragment Sequence No.
m_headerOffset = 0U; m_lastFragment = (header[8U] & 0x08U) == 0x08U; // Last Fragment Flag
m_headerOffset = header[9U] & 0x3FU; // Data Header Offset
break;
case PDU_FMT_RSP:
m_ackNeeded = false;
m_sap = PDU_SAP_USER_DATA;
m_rspClass = (header[1U] >> 6) & 0x03U; // Response Class
m_rspType = (header[1U] >> 3) & 0x07U; // Response Type
m_rspStatus = header[1U] & 0x07U; // Response Status
if (!m_F) {
m_srcLlId = (header[7U] << 16) + (header[8U] << 8) + header[9U]; // Source Logical Link ID
}
break;
case PDU_FMT_AMBT:
m_ambtOpcode = header[7U] & 0x3FU; // AMBT Opcode
m_ambtField8 = header[8U]; // AMBT Field 8 m_ambtField8 = header[8U]; // AMBT Field 8
m_ambtField9 = header[9U]; // AMBT Field 9 m_ambtField9 = header[9U]; // AMBT Field 9
} // fall-thru
else { case PDU_FMT_UNCONFIRMED:
m_sync = (header[8U] & 0x80U) == 0x80U; // Re-synchronize Flag default:
m_ackNeeded = false;
m_n = (header[8U] >> 4) & 0x07U; // Packet Sequence No. m_S = false;
m_seqNo = header[8U] & 0x0FU; // Fragment Sequence No.
m_Ns = 0U;
m_headerOffset = header[9U] & 0x3FU; // Data Header Offset m_fsn = 0U;
m_headerOffset = 0U;
break;
} }
return true; return true;
@ -156,6 +177,10 @@ void DataHeader::encode(uint8_t* data)
uint8_t header[P25_PDU_HEADER_LENGTH_BYTES]; uint8_t header[P25_PDU_HEADER_LENGTH_BYTES];
::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); ::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES);
if (m_fmt == PDU_FMT_UNCONFIRMED || m_fmt == PDU_FMT_RSP) {
m_ackNeeded = false;
}
header[0U] = (m_ackNeeded ? 0x40U : 0x00U) + // Acknowledge Needed header[0U] = (m_ackNeeded ? 0x40U : 0x00U) + // Acknowledge Needed
(m_outbound ? 0x20U : 0x00U) + // Inbound/Outbound (m_outbound ? 0x20U : 0x00U) + // Inbound/Outbound
(m_fmt & 0x1FU); // Packet Format (m_fmt & 0x1FU); // Packet Format
@ -169,29 +194,47 @@ void DataHeader::encode(uint8_t* data)
header[4U] = (m_llId >> 8) & 0xFFU; header[4U] = (m_llId >> 8) & 0xFFU;
header[5U] = (m_llId >> 0) & 0xFFU; header[5U] = (m_llId >> 0) & 0xFFU;
header[6U] = (m_fullMessage ? 0x80U : 0x00U) + // Full Message Flag header[6U] = (m_F ? 0x80U : 0x00U) + // Full Message Flag
(m_blocksToFollow & 0x7FU); // Blocks Frames to Follow (m_blocksToFollow & 0x7FU); // Blocks Frames to Follow
if (m_fmt == PDU_FMT_AMBT) { switch (m_fmt) {
header[7U] = (m_ambtOpcode & 0x3FU); // AMBT Opcode case PDU_FMT_CONFIRMED:
header[8U] = m_ambtField8; // AMBT Field 8
header[9U] = m_ambtField9; // AMBT Field 9
}
else {
header[7U] = (m_padCount & 0x1FU); // Pad Count header[7U] = (m_padCount & 0x1FU); // Pad Count
header[8U] = (m_sync ? 0x80U : 0x00U) + // Re-synchronize Flag header[8U] = (m_S ? 0x80U : 0x00U) + // Re-synchronize Flag
((m_n << 4) & 0x07U) + // Packet Sequence No. ((m_Ns << 4) & 0x07U) + // Packet Sequence No.
(m_seqNo & 0x0F); // Fragment Sequence No. (m_lastFragment ? 0x08U : 0x00U) + // Last Fragment Flag
(m_fsn & 0x07); // Fragment Sequence No.
header[9U] = m_headerOffset & 0x3FU; // Data Header Offset header[9U] = m_headerOffset & 0x3FU; // Data Header Offset
break;
case PDU_FMT_RSP:
header[1U] = ((m_rspClass & 0x03U) << 6) + // Response Class
((m_rspType & 0x07U) << 3) + // Response Type
((m_rspStatus & 0x07U)); // Response Status
if (!m_F) {
header[7U] = (m_srcLlId >> 16) & 0xFFU; // Source Logical Link ID
header[8U] = (m_srcLlId >> 8) & 0xFFU;
header[9U] = (m_srcLlId >> 0) & 0xFFU;
}
break;
case PDU_FMT_AMBT:
header[7U] = (m_ambtOpcode & 0x3FU); // AMBT Opcode
header[8U] = m_ambtField8; // AMBT Field 8
header[9U] = m_ambtField9; // AMBT Field 9
break;
case PDU_FMT_UNCONFIRMED:
default:
header[8U] = 0x00U;
header[9U] = m_headerOffset & 0x3FU; // Data Header Offset
break;
} }
// compute CRC-CCITT 16 // compute CRC-CCITT 16
edac::CRC::addCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES); edac::CRC::addCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES);
#if DEBUG_P25_PDU_DATA #if DEBUG_P25_PDU_DATA
Utils::dump(1U, "P25, DataHeader::encode(), PDU Header Data", data, P25_PDU_HEADER_LENGTH_BYTES); Utils::dump(1U, "P25, DataHeader::encode(), PDU Header Data", header, P25_PDU_HEADER_LENGTH_BYTES);
#endif #endif
// encode 1/2 rate Trellis // encode 1/2 rate Trellis
@ -212,19 +255,28 @@ void DataHeader::reset()
m_mfId = P25_MFG_STANDARD; m_mfId = P25_MFG_STANDARD;
m_llId = 0U; m_llId = 0U;
m_fullMessage = false; m_F = true;
m_blocksToFollow = 0U; m_blocksToFollow = 0U;
m_padCount = 0U; m_padCount = 0U;
m_dataOctets = 0U; m_dataOctets = 0U;
m_sync = false; m_S = false;
m_n = 0U; m_Ns = 0U;
m_seqNo = 0U; m_fsn = 0U;
m_lastFragment = true;
m_headerOffset = 0U; m_headerOffset = 0U;
m_srcLlId = 0U;
m_rspClass = PDU_ACK_CLASS_NACK;
m_rspType = PDU_ACK_TYPE_NACK_ILLEGAL;
m_rspStatus = 0U;
m_ambtOpcode = 0U; m_ambtOpcode = 0U;
m_ambtField8 = 0U;
m_ambtField9 = 0U;
} }
/// <summary>Gets the total number of data octets.</summary> /// <summary>Gets the total number of data octets.</summary>

@ -82,16 +82,30 @@ namespace p25
/// <summary>Logical link ID.</summary> /// <summary>Logical link ID.</summary>
__PROPERTY(uint32_t, llId, LLId); __PROPERTY(uint32_t, llId, LLId);
/// <summary>Flag indicating whether or not this data packet is a full message.</summary> /// <summary>Flag indicating whether or not this data packet is a full message.</summary>
__PROPERTY(bool, fullMessage, FullMessage); /// <remarks>When a response header, this represents the extended flag.</summary>
/// <summary></summary> __PROPERTY(bool, F, FullMessage);
__PROPERTY(bool, sync, Sync); /// <summary>Synchronize Flag.</summary>
/// <summary></summary> __PROPERTY(bool, S, Synchronize);
__PROPERTY(uint8_t, n, N); /// <summary>Fragment Sequence Number.</summary>
/// <summary>Data packet sequence number.</summary> __PROPERTY(uint8_t, fsn, FSN);
__PROPERTY(uint8_t, seqNo, SeqNo); /// <summary>Send Sequence Number.</summary>
__PROPERTY(uint8_t, Ns, Ns);
/// <summary>Flag indicating whether or not this is the last fragment in a message.</summary>
__PROPERTY(bool, lastFragment, LastFragment);
/// <summary>Offset of the header.</summary> /// <summary>Offset of the header.</summary>
__PROPERTY(uint8_t, headerOffset, HeaderOffset); __PROPERTY(uint8_t, headerOffset, HeaderOffset);
/** Response Data */
/// <summary>Source Logical link ID.</summary>
__PROPERTY(uint32_t, srcLlId, SrcLLId);
/// <summary>Response class.</summary>
__PROPERTY(uint8_t, rspClass, Class);
/// <summary>Response type.</summary>
__PROPERTY(uint8_t, rspType, Type);
/// <summary>Response status.</summary>
__PROPERTY(uint8_t, rspStatus, Status);
/** AMBT Data */
/// <summary>Alternate Trunking Block Opcode</summary> /// <summary>Alternate Trunking Block Opcode</summary>
__PROPERTY(uint8_t, ambtOpcode, AMBTOpcode); __PROPERTY(uint8_t, ambtOpcode, AMBTOpcode);
/// <summary>Alternate Trunking Block Field 8</summary> /// <summary>Alternate Trunking Block Field 8</summary>

@ -1,198 +0,0 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
/*
* Copyright (C) 2020,2022 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/data/DataRspHeader.h"
#include "edac/CRC.h"
#include "Utils.h"
using namespace p25::data;
using namespace p25;
#include <cstdio>
#include <cassert>
#include <cstring>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DataRspHeader class.
/// </summary>
DataRspHeader::DataRspHeader() :
m_outbound(true),
m_rspClass(PDU_ACK_CLASS_NACK),
m_rspType(PDU_ACK_TYPE_NACK_ILLEGAL),
m_rspStatus(0U),
m_mfId(P25_MFG_STANDARD),
m_llId(0U),
m_srcLlId(0U),
m_extended(true),
m_trellis(),
m_blocksToFollow(0U),
m_dataOctets(0U)
{
/* stub */
}
/// <summary>
/// Finalizes a instance of the DataRspHeader class.
/// </summary>
DataRspHeader::~DataRspHeader()
{
/* stub */
}
/// <summary>
/// Decodes P25 PDU data response header.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if PDU data response header was decoded, otherwise false.</returns>
bool DataRspHeader::decode(const uint8_t* data)
{
assert(data != NULL);
uint8_t header[P25_PDU_HEADER_LENGTH_BYTES];
::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES);
// decode 1/2 rate Trellis & check CRC-CCITT 16
bool valid = m_trellis.decode12(data, header);
if (valid)
valid = edac::CRC::checkCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES);
if (!valid) {
return false;
}
#if DEBUG_P25_PDU_DATA
Utils::dump(1U, "P25, DataRspHeader::decode(), PDU Response Header Data", data, P25_PDU_HEADER_LENGTH_BYTES);
#endif
m_outbound = (header[0U] & 0x20U) == 0x20U; // Inbound/Outbound
m_rspClass = (header[1U] >> 6) & 0x03U; // Response Class
m_rspType = (header[1U] >> 3) & 0x07U; // Response Type
m_rspStatus = header[1U] & 0x07U; // Response Status
m_mfId = header[2U]; // Mfg Id.
m_llId = (header[3U] << 16) + (header[4U] << 8) + header[5U]; // Logical Link ID
m_extended = (header[6U] & 0x80U) == 0x80U; // Extended Addressing
m_blocksToFollow = header[6U] & 0x7FU; // Block Frames to Follow
m_srcLlId = (header[7U] << 16) + (header[8U] << 8) + header[9U]; // Source Logical Link ID
return true;
}
/// <summary>
/// Encodes P25 PDU data response header.
/// </summary>
/// <param name="data"></param>
void DataRspHeader::encode(uint8_t* data)
{
assert(data != NULL);
uint8_t header[P25_PDU_HEADER_LENGTH_BYTES];
::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES);
header[0U] = PDU_FMT_RSP;
header[0U] = (m_outbound ? 0x20U : 0x00U) + // Inbound/Outbound
(PDU_FMT_RSP & 0x1FU); // Packet Format
header[1U] = ((m_rspClass & 0x03U) << 6) + // Response Class
((m_rspType & 0x07U) << 3) + // Response Type
((m_rspStatus & 0x07U)); // Response Status
header[2U] = m_mfId; // Mfg Id.
header[3U] = (m_llId >> 16) & 0xFFU; // Logical Link ID
header[4U] = (m_llId >> 8) & 0xFFU;
header[5U] = (m_llId >> 0) & 0xFFU;
header[6U] = (m_extended ? 0x80U : 0x00U) + // Extended Addressing
(m_blocksToFollow & 0x7FU); // Blocks Frames to Follow
header[7U] = (m_srcLlId >> 16) & 0xFFU; // Source Logical Link ID
header[8U] = (m_srcLlId >> 8) & 0xFFU;
header[9U] = (m_srcLlId >> 0) & 0xFFU;
// compute CRC-CCITT 16
edac::CRC::addCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES);
#if DEBUG_P25_PDU_DATA
Utils::dump(1U, "P25, DataRspHeader::encode(), PDU Response Header Data", header, P25_PDU_HEADER_LENGTH_BYTES);
#endif
// encode 1/2 rate Trellis
m_trellis.encode12(header, data);
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DataRspHeader::reset()
{
m_outbound = false;
m_rspClass = PDU_ACK_CLASS_NACK;
m_rspType = PDU_ACK_TYPE_NACK_ILLEGAL;
m_rspStatus = 0U;
m_mfId = P25_MFG_STANDARD;
m_llId = 0U;
m_srcLlId = 0U;
m_extended = true;
m_blocksToFollow = 0U;
m_dataOctets = 0U;
}
/// <summary>Gets the total number of data octets.</summary>
/// <returns></returns>
uint32_t DataRspHeader::getDataOctets() const
{
return m_dataOctets;
}
/** Common Data */
/// <summary>Sets the total number of blocks to follow this header.</summary>
/// <param name="blocksToFollow"></param>
void DataRspHeader::setBlocksToFollow(uint8_t blocksToFollow)
{
m_blocksToFollow = blocksToFollow;
// recalculate count of data octets
m_dataOctets = 16 * m_blocksToFollow - 4;
}
/// <summary>Gets the total number of blocks to follow this header.</summary>
/// <returns></returns>
uint8_t DataRspHeader::getBlocksToFollow() const
{
return m_blocksToFollow;
}

@ -1,94 +0,0 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
/*
* Copyright (C) 2020 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(__P25_DATA__DATA_RSP_HEADER_H__)
#define __P25_DATA__DATA_RSP_HEADER_H__
#include "Defines.h"
#include "p25/edac/Trellis.h"
#include <string>
namespace p25
{
namespace data
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents the data response header for PDU P25 packets.
// ---------------------------------------------------------------------------
class HOST_SW_API DataRspHeader {
public:
/// <summary>Initializes a new instance of the DataRspHeader class.</summary>
DataRspHeader();
/// <summary>Finalizes a instance of the DataRspHeader class.</summary>
~DataRspHeader();
/// <summary>Decodes P25 PDU data response header.</summary>
bool decode(const uint8_t* data);
/// <summary>Encodes P25 PDU data response header.</summary>
void encode(uint8_t* data);
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary>Gets the total number of data octets.</summary>
uint32_t getDataOctets() const;
/** Common Data */
/// <summary>Sets the total number of blocks to follow this header.</summary>
void setBlocksToFollow(uint8_t blocksToFollow);
/// <summary>Gets the total number of blocks to follow this header.</summary>
uint8_t getBlocksToFollow() const;
public:
/// <summary>Flag indicating if this is an outbound data packet.</summary>
__PROPERTY(bool, outbound, Outbound);
/// <summary>Response class.</summary>
__PROPERTY(uint8_t, rspClass, Class);
/// <summary>Response type.</summary>
__PROPERTY(uint8_t, rspType, Type);
/// <summary>Response status.</summary>
__PROPERTY(uint8_t, rspStatus, Status);
/// <summary>Manufacturer ID.</summary>
__PROPERTY(uint8_t, mfId, MFId);
/// <summary>Logical link ID.</summary>
__PROPERTY(uint32_t, llId, LLId);
/// <summary>Source Logical link ID.</summary>
__PROPERTY(uint32_t, srcLlId, SrcLLId);
/// <summary>Flag indicating whether or not this response packet is to extended addressing.</summary>
__PROPERTY(bool, extended, Extended);
private:
edac::Trellis m_trellis;
uint8_t m_blocksToFollow;
uint32_t m_dataOctets;
};
} // namespace data
} // namespace p25
#endif // __P25_DATA__DATA_RSP_HEADER_H__
Loading…
Cancel
Save

Powered by TurnKey Linux.