/** * 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) 2017-2022 by Bryan Biedenkapp N2PLL * Copyright (C) 2022 by Jason- UWU - TIME_DATE_ANN & RAD_MON_CMD * * 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/lc/TSBK.h" #include "p25/P25Utils.h" #include "edac/CRC.h" #include "HostMain.h" #include "Log.h" #include "Utils.h" using namespace p25::lc; using namespace p25; #include #include #include #include #include #include // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a copy instance of the TSBK class. /// /// TSBK::TSBK(const TSBK& data) : TSBK() { copy(data); } /// /// Initializes a new instance of the TSBK class. /// /// /// TSBK::TSBK(SiteData siteData, ::lookups::IdenTable entry) : TSBK(siteData) { m_siteIdenEntry = entry; } /// /// Initializes a new instance of the TSBK class. /// /// /// /// TSBK::TSBK(SiteData siteData, ::lookups::IdenTable entry, bool verbose) : TSBK(siteData, entry, verbose, false) { m_verbose = verbose; } /// /// Initializes a new instance of the TSBK class. /// /// /// /// TSBK::TSBK(SiteData siteData, ::lookups::IdenTable entry, bool verbose, bool warnCRC) : TSBK(siteData) { m_warnCRC = warnCRC; m_siteIdenEntry = entry; } /// /// Initializes a new instance of the TSBK class. /// /// TSBK::TSBK(LC* lc) : TSBK(lc->siteData()) { m_protect = lc->m_protect; m_lco = lc->m_lco; m_mfId = lc->m_mfId; m_srcId = lc->m_srcId; m_dstId = lc->m_dstId; m_grpVchNo = lc->m_grpVchNo; m_emergency = lc->m_emergency; m_encrypted = lc->m_encrypted; m_priority = lc->m_priority; m_group = lc->m_group; } /// /// Finalizes a instance of TSBK class. /// TSBK::~TSBK() { if (m_authRes != NULL) { delete[] m_authRes; m_authRes = NULL; } if (m_authRS != NULL) { delete[] m_authRS; m_authRS = NULL; } if (m_authRC != NULL) { delete[] m_authRC; m_authRC = NULL; } delete[] m_siteCallsign; } /// /// Equals operator. /// /// /// TSBK& TSBK::operator=(const TSBK& data) { if (this != &data) { copy(data); } return *this; } /// /// Decode a alternate trunking signalling block. /// /// /// /// True, if TSBK was decoded, otherwise false. bool TSBK::decodeMBT(const data::DataHeader dataHeader, const data::DataBlock* blocks) { assert(blocks != NULL); if (dataHeader.getFormat() != PDU_FMT_AMBT) { LogError(LOG_P25, "TSBK::decodeMBT(), PDU is not a AMBT PDU"); return false; } if (dataHeader.getBlocksToFollow() == 0U) { LogError(LOG_P25, "TSBK::decodeMBT(), PDU contains no data blocks"); return false; } m_lco = dataHeader.getAMBTOpcode(); // LCO m_lastBlock = true; m_mfId = dataHeader.getMFId(); // Mfg Id. if (dataHeader.getOutbound()) { LogWarning(LOG_P25, "TSBK::decodeMBT(), MBT is an outbound MBT?, mfId = $%02X, lco = $%02X", m_mfId, m_lco); } // get PDU block data uint8_t pduUserData[P25_PDU_UNCONFIRMED_LENGTH_BYTES * dataHeader.getBlocksToFollow()]; ::memset(pduUserData, 0x00U, P25_PDU_UNCONFIRMED_LENGTH_BYTES * dataHeader.getBlocksToFollow()); uint32_t dataOffset = 0U; for (uint8_t i = 0; i < dataHeader.getBlocksToFollow(); i++) { uint32_t len = blocks[i].getData(pduUserData + dataOffset); if (len != P25_PDU_UNCONFIRMED_LENGTH_BYTES) { LogError(LOG_P25, "TSBK::decodeMBT(), failed to read PDU data block"); return false; } dataOffset += P25_PDU_UNCONFIRMED_LENGTH_BYTES; } ulong64_t tsbkValue = 0U; // combine bytes into rs value tsbkValue = dataHeader.getAMBTField8(); tsbkValue = (tsbkValue << 8) + dataHeader.getAMBTField9(); tsbkValue = (tsbkValue << 8) + pduUserData[0U]; tsbkValue = (tsbkValue << 8) + pduUserData[1U]; tsbkValue = (tsbkValue << 8) + pduUserData[2U]; tsbkValue = (tsbkValue << 8) + pduUserData[3U]; tsbkValue = (tsbkValue << 8) + pduUserData[4U]; tsbkValue = (tsbkValue << 8) + pduUserData[5U]; // Motorola P25 vendor opcodes if (m_mfId == P25_MFG_MOT) { switch (m_lco) { case TSBK_IOSP_GRP_VCH: case TSBK_IOSP_UU_VCH: case TSBK_IOSP_UU_ANS: case TSBK_IOSP_TELE_INT_ANS: case TSBK_IOSP_STS_UPDT: case TSBK_IOSP_STS_Q: case TSBK_IOSP_MSG_UPDT: case TSBK_IOSP_CALL_ALRT: case TSBK_IOSP_ACK_RSP: case TSBK_IOSP_GRP_AFF: case TSBK_IOSP_U_REG: case TSBK_ISP_CAN_SRV_REQ: case TSBK_ISP_GRP_AFF_Q_RSP: case TSBK_OSP_DENY_RSP: case TSBK_OSP_QUE_RSP: case TSBK_ISP_U_DEREG_REQ: case TSBK_OSP_U_DEREG_ACK: case TSBK_ISP_LOC_REG_REQ: m_mfId = P25_MFG_STANDARD; break; default: LogError(LOG_P25, "TSBK::decodeMBT(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); break; } if (m_mfId == P25_MFG_MOT) { return true; } else { m_mfId = dataHeader.getMFId(); } } // standard P25 reference opcodes switch (m_lco) { case TSBK_IOSP_STS_UPDT: m_statusValue = (uint8_t)((tsbkValue >> 48) & 0xFFFFU); // Message Value m_netId = (uint32_t)((tsbkValue >> 28) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 16) & 0xFFFU); // System ID m_dstId = (uint32_t)(((tsbkValue) & 0xFFFFU) << 8) + pduUserData[6U]; // Target Radio Address m_srcId = dataHeader.getLLId(); // Source Radio Address break; case TSBK_IOSP_MSG_UPDT: m_messageValue = (uint32_t)((tsbkValue >> 48) & 0xFFFFU); // Message Value m_netId = (uint32_t)((tsbkValue >> 28) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 16) & 0xFFFU); // System ID m_dstId = (uint32_t)(((tsbkValue) & 0xFFFFU) << 8) + pduUserData[6U]; // Target Radio Address m_srcId = dataHeader.getLLId(); // Source Radio Address break; case TSBK_IOSP_CALL_ALRT: m_netId = (uint32_t)((tsbkValue >> 44) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 32) & 0xFFFU); // System ID m_dstId = (uint32_t)((tsbkValue >> 8) & 0xFFFFFFU); // Target Radio Address m_srcId = dataHeader.getLLId(); // Source Radio Address break; case TSBK_IOSP_ACK_RSP: m_aivFlag = false; m_service = (uint8_t)((tsbkValue >> 56) & 0x3FU); // Service Type m_netId = (uint32_t)((tsbkValue >> 36) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 24) & 0xFFFU); // System ID m_dstId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Target Radio Address m_srcId = dataHeader.getLLId(); // Source Radio Address break; case TSBK_IOSP_GRP_AFF: m_netId = (uint32_t)((tsbkValue >> 44) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 32) & 0xFFFU); // System ID m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Talkgroup Address m_srcId = dataHeader.getLLId(); // Source Radio Address break; case TSBK_ISP_CAN_SRV_REQ: m_aivFlag = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Additional Info. Flag m_service = (uint8_t)((tsbkValue >> 56) & 0x3FU); // Service Type m_response = (uint8_t)((tsbkValue >> 48) & 0xFFU); // Reason m_netId = (uint32_t)((tsbkValue >> 20) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 8) & 0xFFFU); // System ID m_dstId = (uint32_t)((((tsbkValue) & 0xFFU) << 16) + // Target Radio Address (pduUserData[6U] << 8) + (pduUserData[7U])); m_srcId = dataHeader.getLLId(); // Source Radio Address break; case TSBK_IOSP_EXT_FNCT: m_netId = (uint32_t)((tsbkValue >> 44) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 32) & 0xFFFU); // System ID m_extendedFunction = (uint32_t)(((tsbkValue) & 0xFFFFU) << 8) + // Extended Function pduUserData[6U]; m_srcId = dataHeader.getLLId(); // Source Radio Address break; case TSBK_ISP_AUTH_RESP_M: { if (dataHeader.getBlocksToFollow() != 2) { LogError(LOG_P25, "TSBK::decodeMBT(), PDU does not contain the appropriate amount of data blocks"); return false; } m_netId = (uint32_t)((tsbkValue >> 44) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 32) & 0xFFFU); // System ID m_srcId = dataHeader.getLLId(); // Source Radio Address /** Block 1 */ m_authRC[4U] = (uint8_t)pduUserData[5U] & 0xFFU; // Random Challenge b4 m_authRC[3U] = (uint8_t)pduUserData[6U] & 0xFFU; // Random Challenge b3 m_authRC[2U] = (uint8_t)pduUserData[7U] & 0xFFU; // Random Challenge b2 m_authRC[1U] = (uint8_t)pduUserData[8U] & 0xFFU; // Random Challenge b1 m_authRC[0U] = (uint8_t)pduUserData[9U] & 0xFFU; // Random Challenge b0 m_authRes[3U] = (uint8_t)pduUserData[10U] & 0xFFU; // Result b3 m_authRes[2U] = (uint8_t)pduUserData[11U] & 0xFFU; // Result b2 /** Block 2 */ m_authRes[1U] = (uint8_t)pduUserData[12U] & 0xFFU; // Result b1 m_authRes[0U] = (uint8_t)pduUserData[13U] & 0xFFU; // Result b0 m_authStandalone = ((pduUserData[14U] & 0xFFU) & 0x01U) == 0x01U; // Authentication Standalone Flag } break; case TSBK_ISP_AUTH_SU_DMD: m_netId = (uint32_t)((tsbkValue >> 44) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 32) & 0xFFFU); // System ID m_srcId = dataHeader.getLLId(); // Source Radio Address break; default: LogError(LOG_P25, "TSBK::decodeMBT(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); break; } return true; } /// /// Encode a alternate trunking signalling block. /// /// /// void TSBK::encodeMBT(data::DataHeader& dataHeader, uint8_t* pduUserData) { assert(pduUserData != NULL); dataHeader.setFormat(PDU_FMT_AMBT); dataHeader.setMFId(m_mfId); dataHeader.setAckNeeded(false); dataHeader.setOutbound(true); dataHeader.setSAP(PDU_SAP_TRUNK_CTRL); dataHeader.setLLId(m_srcId); dataHeader.setFullMessage(true); dataHeader.setBlocksToFollow(1U); dataHeader.setAMBTOpcode(m_lco); // standard P25 reference opcodes switch (m_lco) { case TSBK_OSP_AUTH_DMD: { dataHeader.setBlocksToFollow(2U); dataHeader.setAMBTField8((m_netId >> 12) & 0xFFU); // Network ID (b19-12) dataHeader.setAMBTField9((m_netId >> 4) & 0xFFU); // Network ID (b11-b4) /** Block 1 */ pduUserData[0U] = ((m_netId & 0x0FU) << 4) + ((m_sysId >> 8) & 0xFFU); // Network ID (b3-b0) + System ID (b11-b8) pduUserData[1U] = (m_sysId & 0xFFU); // System ID (b7-b0) __SET_UINT16(m_dstId, pduUserData, 2U); // Target Radio Address pduUserData[5U] = m_authRS[9U]; // Random Salt b9 pduUserData[6U] = m_authRS[8U]; // Random Salt b8 pduUserData[7U] = m_authRS[7U]; // Random Salt b7 pduUserData[8U] = m_authRS[6U]; // Random Salt b6 pduUserData[9U] = m_authRS[5U]; // Random Salt b5 pduUserData[10U] = m_authRS[4U]; // Random Salt b4 pduUserData[11U] = m_authRS[3U]; // Random Salt b3 /** Block 2 */ pduUserData[12U] = m_authRS[2U]; // Random Salt b2 pduUserData[13U] = m_authRS[1U]; // Random Salt b1 pduUserData[14U] = m_authRS[0U]; // Random Salt b0 pduUserData[15U] = m_authRC[4U]; // Random Challenge b4 pduUserData[16U] = m_authRC[3U]; // Random Challenge b3 pduUserData[17U] = m_authRC[2U]; // Random Challenge b2 pduUserData[18U] = m_authRC[1U]; // Random Challenge b1 pduUserData[19U] = m_authRC[0U]; // Random Challenge b0 } break; case TSBK_OSP_RFSS_STS_BCAST: { // pack LRA and system ID into LLID uint32_t llId = m_siteData.lra(); // Location Registration Area llId = (llId << 12) + m_siteData.siteId(); // System ID if (m_siteData.netActive()) { llId |= 0x1000U; // Network Active Flag } dataHeader.setLLId(llId); /** Block 1 */ pduUserData[0U] = (m_siteData.rfssId()) & 0xFFU; // RF Sub-System ID pduUserData[1U] = (m_siteData.siteId()) & 0xFFU; // Site ID pduUserData[2U] = ((m_siteData.channelId() & 0x0FU) << 4) + // Transmit Channel ID & Channel Number MSB ((m_siteData.channelNo() >> 8) & 0xFFU); pduUserData[3U] = (m_siteData.channelNo() >> 0) & 0xFFU; // Transmit Channel Number LSB pduUserData[4U] = ((m_siteData.channelId() & 0x0FU) << 4) + // Receive Channel ID & Channel Number MSB ((m_siteData.channelNo() >> 8) & 0xFFU); pduUserData[5U] = (m_siteData.channelNo() >> 0) & 0xFFU; // Receive Channel Number LSB pduUserData[6U] = m_siteData.serviceClass(); // System Service Class } break; case TSBK_OSP_NET_STS_BCAST: { // pack LRA and system ID into LLID uint32_t llId = m_siteData.lra(); // Location Registration Area llId = (llId << 12) + m_siteData.siteId(); // System ID dataHeader.setLLId(llId); /** Block 1 */ pduUserData[0U] = (m_siteData.netId() >> 12) & 0xFFU; // Network ID (b19-12) pduUserData[1U] = (m_siteData.netId() >> 4) & 0xFFU; // Network ID (b11-b4) pduUserData[2U] = (m_siteData.netId() & 0x0FU) << 4; // Network ID (b3-b0) pduUserData[3U] = ((m_siteData.channelId() & 0x0FU) << 4) + // Transmit Channel ID & Channel Number MSB ((m_siteData.channelNo() >> 8) & 0xFFU); pduUserData[4U] = (m_siteData.channelNo() >> 0) & 0xFFU; // Transmit Channel Number LSB pduUserData[5U] = ((m_siteData.channelId() & 0x0FU) << 4) + // Receive Channel ID & Channel Number MSB ((m_siteData.channelNo() >> 8) & 0xFFU); pduUserData[6U] = (m_siteData.channelNo() >> 0) & 0xFFU; // Receive Channel Number LSB pduUserData[7U] = m_siteData.serviceClass(); // System Service Class } break; case TSBK_OSP_ADJ_STS_BCAST: { if ((m_adjRfssId != 0U) && (m_adjSiteId != 0U) && (m_adjChannelNo != 0U)) { if (m_adjSysId == 0U) { m_adjSysId = m_siteData.sysId(); } // pack LRA, CFVA and system ID into LLID uint32_t llId = m_siteData.lra(); // Location Registration Area llId = (llId << 8) + m_adjCFVA; // CFVA llId = (llId << 4) + m_siteData.siteId(); // System ID dataHeader.setLLId(llId); dataHeader.setAMBTField8(m_adjRfssId); // RF Sub-System ID dataHeader.setAMBTField9(m_adjSiteId); // Site ID /** Block 1 */ pduUserData[0U] = ((m_adjChannelId & 0x0FU) << 4) + // Transmit Channel ID & Channel Number MSB ((m_adjChannelNo >> 8) & 0xFFU); pduUserData[1U] = (m_adjChannelNo >> 0) & 0xFFU; // Transmit Channel Number LSB pduUserData[2U] = ((m_adjChannelId & 0x0FU) << 4) + // Receive Channel ID & Channel Number MSB ((m_adjChannelNo >> 8) & 0xFFU); pduUserData[3U] = (m_adjChannelNo >> 0) & 0xFFU; // Receive Channel Number LSB pduUserData[4U] = m_adjServiceClass; // System Service Class pduUserData[5U] = (m_siteData.netId() >> 12) & 0xFFU; // Network ID (b19-12) pduUserData[6U] = (m_siteData.netId() >> 4) & 0xFFU; // Network ID (b11-b4) pduUserData[7U] = (m_siteData.netId() & 0x0FU) << 4; // Network ID (b3-b0) } else { LogError(LOG_P25, "TSBK::encodeMBT(), invalid values for OSP_ADJ_STS_BCAST, adjRfssId = $%02X, adjSiteId = $%02X, adjChannelId = %u, adjChannelNo = $%02X, adjSvcClass = $%02X", m_adjRfssId, m_adjSiteId, m_adjChannelId, m_adjChannelNo, m_adjServiceClass); return; // blatently ignore creating this TSBK } } break; default: LogError(LOG_P25, "TSBK::encodeMBT(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); break; } // generate packet CRC-32 and set data blocks if (dataHeader.getBlocksToFollow() > 1U) { edac::CRC::addCRC32(pduUserData, P25_PDU_UNCONFIRMED_LENGTH_BYTES * dataHeader.getBlocksToFollow()); } else { edac::CRC::addCRC32(pduUserData, P25_PDU_UNCONFIRMED_LENGTH_BYTES); } } /// /// Decode a trunking signalling block. /// /// /// /// True, if TSBK was decoded, otherwise false. bool TSBK::decode(const uint8_t* data, bool rawTSBK) { assert(data != NULL); uint8_t tsbk[P25_TSBK_LENGTH_BYTES + 1U]; if (rawTSBK) { ::memcpy(tsbk, data, P25_TSBK_LENGTH_BYTES); bool ret = edac::CRC::checkCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); if (!ret) { if (m_warnCRC) { LogWarning(LOG_P25, "TSBK::decode(), failed CRC CCITT-162 check"); ret = true; // ignore CRC error } else { LogError(LOG_P25, "TSBK::decode(), failed CRC CCITT-162 check"); } } } else { // deinterleave uint8_t raw[P25_TSBK_FEC_LENGTH_BYTES]; P25Utils::decode(data, raw, 114U, 318U); // decode 1/2 rate Trellis & check CRC-CCITT 16 try { bool ret = m_trellis.decode12(raw, tsbk); if (!ret) { LogError(LOG_P25, "TSBK::decode(), failed to decode Trellis 1/2 rate coding"); } if (ret) { ret = edac::CRC::checkCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); if (!ret) { if (m_warnCRC) { LogWarning(LOG_P25, "TSBK::decode(), failed CRC CCITT-162 check"); ret = true; // ignore CRC error } else { LogError(LOG_P25, "TSBK::decode(), failed CRC CCITT-162 check"); } } } if (!ret) return false; } catch (...) { Utils::dump(2U, "P25, decoding excepted with input data", tsbk, P25_TSBK_LENGTH_BYTES); return false; } } if (m_verbose) { Utils::dump(2U, "Decoded TSBK", tsbk, P25_TSBK_LENGTH_BYTES); } m_lco = tsbk[0U] & 0x3F; // LCO m_lastBlock = (tsbk[0U] & 0x80U) == 0x80U; // Last Block Marker m_mfId = tsbk[1U]; // Mfg Id. ulong64_t tsbkValue = 0U; // combine bytes into rs value tsbkValue = tsbk[2U]; tsbkValue = (tsbkValue << 8) + tsbk[3U]; tsbkValue = (tsbkValue << 8) + tsbk[4U]; tsbkValue = (tsbkValue << 8) + tsbk[5U]; tsbkValue = (tsbkValue << 8) + tsbk[6U]; tsbkValue = (tsbkValue << 8) + tsbk[7U]; tsbkValue = (tsbkValue << 8) + tsbk[8U]; tsbkValue = (tsbkValue << 8) + tsbk[9U]; // Motorola P25 vendor opcodes if (m_mfId == P25_MFG_MOT) { switch (m_lco) { case TSBK_IOSP_GRP_VCH: case TSBK_IOSP_UU_VCH: case TSBK_IOSP_UU_ANS: case TSBK_IOSP_TELE_INT_ANS: case TSBK_IOSP_STS_UPDT: case TSBK_IOSP_STS_Q: case TSBK_IOSP_MSG_UPDT: case TSBK_IOSP_CALL_ALRT: case TSBK_IOSP_ACK_RSP: case TSBK_IOSP_GRP_AFF: case TSBK_IOSP_U_REG: case TSBK_ISP_CAN_SRV_REQ: case TSBK_ISP_GRP_AFF_Q_RSP: case TSBK_OSP_DENY_RSP: case TSBK_OSP_QUE_RSP: case TSBK_ISP_U_DEREG_REQ: case TSBK_OSP_U_DEREG_ACK: case TSBK_ISP_LOC_REG_REQ: m_mfId = P25_MFG_STANDARD; break; default: LogError(LOG_P25, "TSBK::decode(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); break; } if (m_mfId == P25_MFG_MOT) { return true; } else { m_mfId = tsbk[1U]; } } // internal P25 vendor opcodes if (m_mfId == P25_MFG_DVM) { switch (m_lco) { case LC_CALL_TERM: m_grpVchId = ((tsbkValue >> 52) & 0x0FU); // Channel ID m_grpVchNo = ((tsbkValue >> 40) & 0xFFFU); // Channel Number m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; default: m_mfId = P25_MFG_STANDARD; break; } if (m_mfId == P25_MFG_DVM) { return true; } else { m_mfId = tsbk[1U]; } } // standard P25 reference opcodes switch (m_lco) { case TSBK_IOSP_GRP_VCH: m_emergency = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag m_encrypted = (((tsbkValue >> 56) & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag m_priority = (((tsbkValue >> 56) & 0xFFU) & 0x07U); // Priority m_grpVchId = ((tsbkValue >> 52) & 0x0FU); // Channel ID m_grpVchNo = ((tsbkValue >> 40) & 0xFFFU); // Channel Number m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_UU_VCH: m_emergency = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag m_encrypted = (((tsbkValue >> 56) & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag m_priority = (((tsbkValue >> 56) & 0xFFU) & 0x07U); // Priority m_grpVchId = ((tsbkValue >> 52) & 0x0FU); // Channel ID m_grpVchNo = ((tsbkValue >> 40) & 0xFFFU); // Channel Number m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_UU_ANS: m_emergency = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag m_encrypted = (((tsbkValue >> 56) & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag m_priority = (((tsbkValue >> 56) & 0xFFU) & 0x07U); // Priority m_response = (uint8_t)((tsbkValue >> 48) & 0xFFU); // Answer Response m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_TELE_INT_ANS: m_emergency = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag m_encrypted = (((tsbkValue >> 56) & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag m_priority = (((tsbkValue >> 56) & 0xFFU) & 0x07U); // Priority m_response = (uint8_t)((tsbkValue >> 48) & 0xFFU); // Answer Response m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_SNDCP_CH_REQ: m_dataServiceOptions = (uint8_t)((tsbkValue >> 56) & 0xFFU); // Data Service Options m_dataAccessControl = (uint32_t)((tsbkValue >> 40) & 0xFFFFFFFFU); // Data Access Control m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_STS_UPDT: m_statusValue = (uint8_t)((tsbkValue >> 56) & 0xFFU); // Status Value m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_MSG_UPDT: m_messageValue = (uint32_t)((tsbkValue >> 48) & 0xFFFFU); // Message Value m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_RAD_MON_REQ: m_txMult = (uint8_t)((tsbkValue >> 48) & 0x3U); // TX Multiplier m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_CALL_ALRT: m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_ACK_RSP: m_aivFlag = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Additional Info. Flag m_service = (uint8_t)((tsbkValue >> 56) & 0x3FU); // Service Type m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_EMERG_ALRM_REQ: // TSBK_OSP_DENY_RSP /* ** these are used by TSBK_OSP_DENY_RSP; best way to check is for m_response > 0 */ m_aivFlag = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Additional Info. Flag m_service = (uint8_t)((tsbkValue >> 56) & 0x3FU); // Service Type m_response = (uint8_t)((tsbkValue >> 48) & 0xFFU); // Reason if (m_response == 0U) { m_emergency = true; } else { m_emergency = false; } m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_EXT_FNCT: m_extendedFunction = (uint32_t)((tsbkValue >> 48) & 0xFFFFU); // Extended Function m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Argument m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Target Radio Address break; case TSBK_IOSP_GRP_AFF: m_sysId = (uint32_t)((tsbkValue >> 40) & 0xFFFU); // System ID m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Talkgroup Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_IOSP_U_REG: m_netId = (uint32_t)((tsbkValue >> 36) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 24) & 0xFFFU); // System ID m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_CAN_SRV_REQ: m_aivFlag = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Additional Info. Flag m_service = (uint8_t)((tsbkValue >> 56) & 0x3FU); // Service Type m_response = (uint8_t)((tsbkValue >> 48) & 0xFFU); // Reason m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_GRP_AFF_Q_RSP: m_patchSuperGroupId = (uint32_t)((tsbkValue >> 40) & 0xFFFFU); // Announcement Group Address m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Talkgroup Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_OSP_QUE_RSP: m_aivFlag = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Additional Info. Flag m_service = (uint8_t)((tsbkValue >> 56) & 0x3FU); // Service Type m_response = (uint8_t)((tsbkValue >> 48) & 0xFFU); // Reason m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_U_DEREG_REQ: case TSBK_OSP_U_DEREG_ACK: m_netId = (uint32_t)((tsbkValue >> 36) & 0xFFFFFU); // Network ID m_sysId = (uint32_t)((tsbkValue >> 24) & 0xFFFU); // System ID m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_LOC_REG_REQ: m_lra = (uint8_t)((tsbkValue >> 40) & 0xFFU); // LRA m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Talkgroup Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_AUTH_RESP: m_authStandalone = (((tsbkValue >> 56) & 0xFFU) & 0x01U) == 0x01U; // Authentication Standalone Flag m_authRes[3U] = (uint8_t)((tsbkValue >> 48) & 0xFFU); // Result b3 m_authRes[2U] = (uint8_t)((tsbkValue >> 40) & 0xFFU); // Result b2 m_authRes[1U] = (uint8_t)((tsbkValue >> 32) & 0xFFU); // Result b1 m_authRes[0U] = (uint8_t)((tsbkValue >> 24) & 0xFFU); // Result b0 m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_AUTH_FNE_RST: m_authSuccess = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Authentication Success Flag m_authStandalone = (((tsbkValue >> 56) & 0xFFU) & 0x01U) == 0x01U; // Authentication Standalone Flag m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_ISP_AUTH_SU_DMD: m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; case TSBK_OSP_ADJ_STS_BCAST: m_adjSysId = (uint32_t)((tsbkValue >> 40) & 0xFFFU); // Site System ID m_adjRfssId = (uint8_t)((tsbkValue >> 32) & 0xFFU); // Site RFSS ID m_adjSiteId = (uint8_t)((tsbkValue >> 24) & 0xFFU); // Site ID m_adjChannelId = (uint8_t)((tsbkValue >> 20) & 0xFU); // Site Channel ID m_adjChannelNo = (uint32_t)((tsbkValue >> 8) & 0xFFFU); // Site Channel Number m_adjServiceClass = (uint8_t)(tsbkValue & 0xFFU); // Site Service Class break; default: LogError(LOG_P25, "TSBK::decode(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); break; } return true; } /// /// Encode a trunking signalling block. /// /// /// /// void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis) { assert(data != NULL); const uint32_t services = (m_siteData.netActive()) ? P25_SYS_SRV_NET_ACTIVE : 0U | P25_SYS_SRV_DEFAULT; std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); time_t tt = std::chrono::system_clock::to_time_t(now); uint8_t tsbk[P25_TSBK_LENGTH_BYTES]; ::memset(tsbk, 0x00U, P25_TSBK_LENGTH_BYTES); ulong64_t tsbkValue = 0U; tsbk[0U] = m_lco; // LCO tsbk[0U] |= (m_lastBlock) ? 0x80U : 0x00U; // Last Block Marker tsbk[1U] = m_mfId; // standard P25 reference opcodes switch (m_lco) { case TSBK_IOSP_GRP_VCH: tsbkValue = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag (m_encrypted ? 0x40U : 0x00U) + // Encrypted Flag (m_priority & 0x07U); // Priority tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_IOSP_UU_VCH: tsbkValue = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag (m_encrypted ? 0x40U : 0x00U) + // Encrypted Flag (m_priority & 0x07U); // Priority tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_IOSP_UU_ANS: tsbkValue = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag (m_encrypted ? 0x40U : 0x00U) + // Encrypted Flag (m_priority & 0x07U); // Priority tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_SNDCP_CH_GNT: { uint32_t calcSpace = (uint32_t)(m_siteIdenEntry.chSpaceKhz() / 0.125); float calcTxOffset = m_siteIdenEntry.txOffsetMhz() * 1000000; uint32_t txFrequency = (uint32_t)((m_siteIdenEntry.baseFrequency() + ((calcSpace * 125) * m_dataChannelNo))); uint32_t rxFrequency = (uint32_t)(txFrequency + calcTxOffset); uint32_t rootFreq = rxFrequency - m_siteIdenEntry.baseFrequency(); uint32_t rxChNo = rootFreq / (m_siteIdenEntry.chSpaceKhz() * 1000); tsbkValue = 0U; tsbkValue = (tsbkValue << 8) + m_dataServiceOptions; // Data Service Options tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (T) ID tsbkValue = (tsbkValue << 12) + m_dataChannelNo; // Channel (T) Number tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (R) ID tsbkValue = (tsbkValue << 12) + (rxChNo & 0xFFFU); // Channel (R) Number tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address } break; case TSBK_OSP_SNDCP_CH_ANN: { uint32_t calcSpace = (uint32_t)(m_siteIdenEntry.chSpaceKhz() / 0.125); float calcTxOffset = m_siteIdenEntry.txOffsetMhz() * 1000000; uint32_t txFrequency = (uint32_t)((m_siteIdenEntry.baseFrequency() + ((calcSpace * 125) * m_siteData.channelNo()))); uint32_t rxFrequency = (uint32_t)(txFrequency + calcTxOffset); uint32_t rootFreq = rxFrequency - m_siteIdenEntry.baseFrequency(); uint32_t rxChNo = rootFreq / (m_siteIdenEntry.chSpaceKhz() * 1000); tsbkValue = 0U; // tsbkValue = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag (m_encrypted ? 0x40U : 0x00U); // Encrypted Flag tsbkValue = (tsbkValue << 8) + (m_sndcpAutoAccess ? 0x80U : 0x00U) + // Autonomous Access (m_sndcpAutoAccess ? 0x40U : 0x00U); // Requested Access tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (T) ID tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel (T) Number tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (R) ID tsbkValue = (tsbkValue << 12) + (rxChNo & 0xFFFU); // Channel (R) Number tsbkValue = (tsbkValue << 16) + m_sndcpDAC; // Data Access Control } break; case TSBK_IOSP_STS_UPDT: tsbkValue = 0U; tsbkValue = (tsbkValue << 16) + m_statusValue; // Status Value tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_IOSP_MSG_UPDT: tsbkValue = 0U; tsbkValue = (tsbkValue << 16) + m_messageValue; // Message Value tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_RAD_MON_CMD: tsbkValue = (tsbkValue << 48) + (m_txMult & 0x3U); // TX Multiplier tsbkValue = (tsbkValue << 24) + (m_srcId & 0xFFFFFFU); // Source Radio Address tsbkValue = tsbkValue + (m_dstId & 0xFFFFFFU); // Target Radio Address break; case TSBK_IOSP_CALL_ALRT: tsbkValue = 0U; tsbkValue = (tsbkValue << 40) + m_dstId; // Target Radio Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_IOSP_ACK_RSP: tsbkValue = (m_service & 0x3F); // Service Type tsbkValue |= (m_aivFlag) ? 0x80U : 0x00U; // Additional Info. Valid Flag tsbkValue |= (m_extendedAddrFlag) ? 0x40U : 0x00U; // Extended Addressing Flag if (m_aivFlag && m_extendedAddrFlag) { tsbkValue = (tsbkValue << 20) + m_siteData.netId(); // Network ID tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID } else { tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address } tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_IOSP_EXT_FNCT: tsbkValue = 0U; tsbkValue = (tsbkValue << 16) + m_extendedFunction; // Extended Function tsbkValue = (tsbkValue << 24) + m_srcId; // Argument tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address break; case TSBK_IOSP_GRP_AFF: tsbkValue = 1U; // Local/Global Affiliation Flag (0 = Local, 1 = Global) tsbkValue = (tsbkValue << 7) + (m_response & 0x3U); // Affiliation Response tsbkValue = (tsbkValue << 16) + (m_patchSuperGroupId & 0xFFFFU); // Announcement Group Address tsbkValue = (tsbkValue << 16) + (m_dstId & 0xFFFFU); // Talkgroup Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_IOSP_U_REG: tsbkValue = 0U; tsbkValue = (tsbkValue << 2) + (m_response & 0x3U); // Unit Registration Response tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID tsbkValue = (tsbkValue << 24) + m_dstId; // Source ID tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_GRP_VCH_GRANT_UPD: tsbkValue = 0U; tsbkValue = m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address tsbkValue = (tsbkValue << 32) + 0; break; case TSBK_OSP_DENY_RSP: // TSBK_ISP_EMERG_ALRM_REQ case TSBK_OSP_QUE_RSP: { if (m_response == 0U) { if (m_lco == TSBK_OSP_QUE_RSP) { LogError(LOG_P25, "TSBK::encode(), invalid values for TSBK_OSP_QUE_RSP, reason = %u", m_response); } else { LogError(LOG_P25, "TSBK::encode(), invalid values for TSBK_OSP_DENY_RSP, reason = %u", m_response); } return; // blatently ignore creating this TSBK } tsbkValue = (m_aivFlag) ? 0x80U : 0x00U; // Additional Info Flag tsbkValue = (tsbkValue << 6) + m_service; // Service Type tsbkValue = (tsbkValue << 8) + m_response; // Deny/Queue Reason if (m_group) { // group deny/queue tsbkValue = (tsbkValue << 8) + 0U; // Call Options tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address } else { // private/individual deny/queue tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address } } break; case TSBK_OSP_SCCB_EXP: tsbkValue = m_siteData.rfssId(); // RF Sub-System ID tsbkValue = (tsbkValue << 8) + m_siteData.siteId(); // Site ID tsbkValue = (tsbkValue << 4) + m_sccbChannelId1; // Channel (T) ID tsbkValue = (tsbkValue << 12) + m_sccbChannelNo; // Channel (T) Number tsbkValue = (tsbkValue << 12) + m_sccbChannelId1; // Channel (R) ID tsbkValue = (tsbkValue << 12) + m_sccbChannelNo; // Channel (R) Number if (m_sccbChannelId1 > 0) { tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class } else { tsbkValue = (tsbkValue << 8) + (P25_SVC_CLS_INVALID); // System Service Class } break; case TSBK_OSP_GRP_AFF_Q: tsbkValue = 0U; tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_LOC_REG_RSP: tsbkValue = 0U; tsbkValue = (tsbkValue << 6) + (m_response & 0x3U); // Registration Response tsbkValue = (tsbkValue << 16) + (m_dstId & 0xFFFFU); // Talkgroup Address tsbkValue = (tsbkValue << 8) + m_siteData.rfssId(); // RF Sub-System ID tsbkValue = (tsbkValue << 8) + m_siteData.sysId(); // Site ID tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_U_REG_CMD: tsbkValue = 0U; tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_U_DEREG_ACK: tsbkValue = 0U; tsbkValue = (tsbkValue << 8) + m_siteData.netId(); // Network ID tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_SYNC_BCAST: { tm local_tm = *gmtime(&tt); uint32_t tmM = (local_tm.tm_mon + 1); uint32_t tmY = (local_tm.tm_year + 1900) - 2000; #if DEBUG_P25_TSBK LogDebug(LOG_P25, "TSBK_OSP_SYNC_BCAST, tmM = %u / %u, tmY = %u / %u", local_tm.tm_mon, tmM, local_tm.tm_year, tmY); #endif // determine LTO and direction (positive or negative) bool negativeLTO = false; uint8_t lto = fabs(m_siteData.lto()) * 2U; // this will cause a bug for half-hour timezone intervals... if (m_siteData.lto() < 0) negativeLTO = true; // mark the LTO as valid if its non-zero bool vl = false; if (lto > 0U) vl = true; uint8_t mc = 0U; // wrap microslot count if necessary if (m_microslotCount > 7999U) m_microslotCount = 0U; tsbkValue = 0x0AU + // US - Unsynced Flag Set / MMU - Microslot/Minute Unlock Flag Set ((mc & 0x03U) >> 1); // Minute Correction MSB tsbkValue = (tsbkValue << 8) + ((mc & 0x01U) << 7) + // Minute Correction LSB (vl ? 0x40U : 0x00U) + // Valid LTO Flag (negativeLTO ? 0x20U : 0x00U) + // Add/Subtract LTO Flag (lto & 0x1F); // LTO // Date tsbkValue = (tsbkValue << 7) + (tmY & 0x7FU); // Number of Years Past 2000 tsbkValue = (tsbkValue << 4) + (tmM & 0x0FU); // Month tsbkValue = (tsbkValue << 5) + (local_tm.tm_mday & 0x1FU); // Day of Month // Time tsbkValue = (tsbkValue << 5) + (local_tm.tm_hour & 0x1FU); // Hour tsbkValue = (tsbkValue << 6) + (local_tm.tm_min & 0x3FU); // Minute tsbkValue = (tsbkValue << 13) + (m_microslotCount & 0x1FFFU); // Microslot Count } break; case TSBK_OSP_AUTH_FNE_RESP: tsbkValue = 0U; tsbkValue = (tsbkValue << 8) + m_authRes[3U]; // Result b3 tsbkValue = (tsbkValue << 8) + m_authRes[2U]; // Result b2 tsbkValue = (tsbkValue << 8) + m_authRes[1U]; // Result b1 tsbkValue = (tsbkValue << 8) + m_authRes[0U]; // Result b0 tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_IDEN_UP_VU: { if ((m_siteIdenEntry.chBandwidthKhz() != 0.0F) && (m_siteIdenEntry.chSpaceKhz() != 0.0F) && (m_siteIdenEntry.txOffsetMhz() != 0.0F) && (m_siteIdenEntry.baseFrequency() != 0U)) { uint32_t calcSpace = (uint32_t)(m_siteIdenEntry.chSpaceKhz() / 0.125); float fCalcTxOffset = (fabs(m_siteIdenEntry.txOffsetMhz()) / m_siteIdenEntry.chSpaceKhz()) * 1000.0F; uint32_t uCalcTxOffset = (uint32_t)fCalcTxOffset; if (m_siteIdenEntry.txOffsetMhz() > 0.0F) uCalcTxOffset |= 0x2000U; // this sets a positive offset ... uint32_t calcBaseFreq = (uint32_t)(m_siteIdenEntry.baseFrequency() / 5); uint8_t chanBw = (m_siteIdenEntry.chBandwidthKhz() >= 12.5F) ? P25_IDEN_UP_VU_BW_125K : P25_IDEN_UP_VU_BW_625K; tsbkValue = m_siteIdenEntry.channelId(); // Channel ID tsbkValue = (tsbkValue << 4) + chanBw; // Channel Bandwidth tsbkValue = (tsbkValue << 14) + uCalcTxOffset; // Transmit Offset tsbkValue = (tsbkValue << 10) + calcSpace; // Channel Spacing tsbkValue = (tsbkValue << 32) + calcBaseFreq; // Base Frequency } else { LogError(LOG_P25, "TSBK::encode(), invalid values for TSBK_OSP_IDEN_UP_VU, baseFrequency = %uHz, txOffsetMhz = %fMHz, chBandwidthKhz = %fKHz, chSpaceKhz = %fKHz", m_siteIdenEntry.baseFrequency(), m_siteIdenEntry.txOffsetMhz(), m_siteIdenEntry.chBandwidthKhz(), m_siteIdenEntry.chSpaceKhz()); return; // blatently ignore creating this TSBK } } break; case TSBK_OSP_SYS_SRV_BCAST: tsbkValue = 0U; // tsbkValue = (tsbkValue << 16) + services; // System Services Available tsbkValue = (tsbkValue << 24) + services; // System Services Supported break; case TSBK_OSP_SCCB: tsbkValue = m_siteData.rfssId(); // RF Sub-System ID tsbkValue = (tsbkValue << 8) + m_siteData.siteId(); // Site ID tsbkValue = (tsbkValue << 16) + m_sccbChannelId1; // SCCB Channel ID 1 if (m_sccbChannelId1 > 0) { tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class } else { tsbkValue = (tsbkValue << 8) + (P25_SVC_CLS_INVALID); // System Service Class } tsbkValue = (tsbkValue << 16) + m_sccbChannelId2; // SCCB Channel ID 2 if (m_sccbChannelId2 > 0) { tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class } else { tsbkValue = (tsbkValue << 8) + (P25_SVC_CLS_INVALID); // System Service Class } break; case TSBK_OSP_RFSS_STS_BCAST: tsbkValue = m_siteData.lra(); // Location Registration Area tsbkValue = (tsbkValue << 4) + (m_siteData.netActive()) ? P25_CFVA_NETWORK : 0U; // CFVA tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID tsbkValue = (tsbkValue << 8) + m_siteData.rfssId(); // RF Sub-System ID tsbkValue = (tsbkValue << 8) + m_siteData.siteId(); // Site ID tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class break; case TSBK_OSP_NET_STS_BCAST: tsbkValue = m_siteData.lra(); // Location Registration Area tsbkValue = (tsbkValue << 20) + m_siteData.netId(); // Network ID tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class break; case TSBK_OSP_ADJ_STS_BCAST: { if ((m_adjRfssId != 0U) && (m_adjSiteId != 0U) && (m_adjChannelNo != 0U)) { if (m_adjSysId == 0U) { m_adjSysId = m_siteData.sysId(); } tsbkValue = m_siteData.lra(); // Location Registration Area tsbkValue = (tsbkValue << 8) + m_adjCFVA; // CFVA tsbkValue = (tsbkValue << 12) + m_adjSysId; // System ID tsbkValue = (tsbkValue << 8) + m_adjRfssId; // RF Sub-System ID tsbkValue = (tsbkValue << 8) + m_adjSiteId; // Site ID tsbkValue = (tsbkValue << 4) + m_adjChannelId; // Channel ID tsbkValue = (tsbkValue << 12) + m_adjChannelNo; // Channel Number tsbkValue = (tsbkValue << 8) + m_adjServiceClass; // System Service Class } else { LogError(LOG_P25, "TSBK::encode(), invalid values for OSP_ADJ_STS_BCAST, adjRfssId = $%02X, adjSiteId = $%02X, adjChannelId = %u, adjChannelNo = $%02X, adjSvcClass = $%02X", m_adjRfssId, m_adjSiteId, m_adjChannelId, m_adjChannelNo, m_adjServiceClass); return; // blatently ignore creating this TSBK } } break; case TSBK_OSP_IDEN_UP: { if ((m_siteIdenEntry.chBandwidthKhz() != 0.0F) && (m_siteIdenEntry.chSpaceKhz() != 0.0F) && (m_siteIdenEntry.txOffsetMhz() != 0.0F) && (m_siteIdenEntry.baseFrequency() != 0U)) { if (m_siteIdenEntry.baseFrequency() < 762000000U) { LogError(LOG_P25, "TSBK::encode(), invalid values for TSBK_OSP_IDEN_UP, baseFrequency = %uHz", m_siteIdenEntry.baseFrequency()); return; // blatently ignore creating this TSBK } uint32_t calcSpace = (uint32_t)(m_siteIdenEntry.chSpaceKhz() / 0.125); float fCalcTxOffset = (fabs(m_siteIdenEntry.txOffsetMhz()) * 1000000.0F) / 250000.0F; uint32_t uCalcTxOffset = (uint32_t)fCalcTxOffset; if (m_siteIdenEntry.txOffsetMhz() > 0.0F) uCalcTxOffset |= 0x2000U; // this sets a positive offset ... uint32_t calcBaseFreq = (uint32_t)(m_siteIdenEntry.baseFrequency() / 5); uint16_t chanBw = (uint16_t)((m_siteIdenEntry.chBandwidthKhz() * 1000) / 125); tsbkValue = m_siteIdenEntry.channelId(); // Channel ID tsbkValue = (tsbkValue << 9) + chanBw; // Channel Bandwidth tsbkValue = (tsbkValue << 9) + uCalcTxOffset; // Transmit Offset tsbkValue = (tsbkValue << 10) + calcSpace; // Channel Spacing tsbkValue = (tsbkValue << 32) + calcBaseFreq; // Base Frequency } else { LogError(LOG_P25, "TSBK::encode(), invalid values for TSBK_OSP_IDEN_UP, baseFrequency = %uHz, txOffsetMhz = %fMHz, chBandwidthKhz = %fKHz, chSpaceKhz = %fKHz", m_siteIdenEntry.baseFrequency(), m_siteIdenEntry.txOffsetMhz(), m_siteIdenEntry.chBandwidthKhz(), m_siteIdenEntry.chSpaceKhz()); return; // blatently ignore creating this TSBK } } break; default: if (m_mfId == P25_MFG_STANDARD) { LogError(LOG_P25, "TSBK::encode(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); } break; } if (!m_vendorSkip) { // Motorola P25 vendor opcodes if (m_mfId == P25_MFG_MOT) { switch (m_lco) { case TSBK_OSP_MOT_GRG_ADD: { if ((m_patchSuperGroupId != 0U)) { tsbkValue = m_patchSuperGroupId; // Patch Super Group Address tsbkValue = (tsbkValue << 16) + m_patchGroup1Id; // Patch Group 1 Address if (m_patchGroup2Id != 0U) { tsbkValue = (tsbkValue << 16) + m_patchGroup2Id; // Patch Group 2 Address } else { tsbkValue = (tsbkValue << 16) + m_patchGroup1Id; // Patch Group 1 Address } if (m_patchGroup3Id != 0U) { tsbkValue = (tsbkValue << 16) + m_patchGroup3Id; // Patch Group 3 Address } else { tsbkValue = (tsbkValue << 16) + m_patchGroup1Id; // Patch Group 1 Address } } // otherwise handled as a TSBK_OSP_MOT_GRG_ADD } break; case TSBK_OSP_MOT_GRG_DEL: { if ((m_patchSuperGroupId != 0U) && (m_patchGroup1Id != 0U)) { tsbkValue = m_patchSuperGroupId; // Patch Super Group Address tsbkValue = (tsbkValue << 16) + m_patchGroup1Id; // Patch Group 1 Address if (m_patchGroup2Id != 0U) { tsbkValue = (tsbkValue << 16) + m_patchGroup2Id; // Patch Group 2 Address } else { tsbkValue = (tsbkValue << 16) + m_patchGroup1Id; // Patch Group 1 Address } if (m_patchGroup3Id != 0U) { tsbkValue = (tsbkValue << 16) + m_patchGroup3Id; // Patch Group 3 Address } else { tsbkValue = (tsbkValue << 16) + m_patchGroup1Id; // Patch Group 1 Address } } else { LogError(LOG_P25, "TSBK::encode(), invalid values for TSBK_OSP_MOT_GRG_DEL, patchSuperGroupId = $%02X, patchGroup1Id = $%02X", m_patchSuperGroupId, m_patchGroup1Id); return; // blatently ignore creating this TSBK } } break; case TSBK_OSP_MOT_GRG_VCH_GRANT: { if (m_patchSuperGroupId != 0U) { tsbkValue = 0U; // Priority tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number tsbkValue = (tsbkValue << 16) + m_patchSuperGroupId; // Patch Supergroup Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address } else { LogError(LOG_P25, "TSBK::encode(), invalid values for TSBK_OSP_MOT_GRG_VCH_GRANT, patchSuperGroupId = $%02X", m_patchSuperGroupId); return; // blatently ignore creating this TSBK } } break; case TSBK_OSP_MOT_GRG_VCH_UPD: tsbkValue = m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 4) + m_siteData.channelNo(); // Channel Number tsbkValue = (tsbkValue << 12) + m_patchGroup1Id; // Patch Group 1 tsbkValue = (tsbkValue << 16) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 4) + m_siteData.channelNo(); // Channel Number tsbkValue = (tsbkValue << 12) + m_patchGroup2Id; // Patch Group 2 break; break; case TSBK_OSP_MOT_CC_BSI: tsbkValue = (m_siteCallsign[0] - 43U) & 0x3F; // Character 0 for (uint8_t i = 1; i < P25_MOT_CALLSIGN_LENGTH_BYTES; i++) { tsbkValue = (tsbkValue << 6) + ((m_siteCallsign[i] - 43U) & 0x3F); // Character 1 - 7 } tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number break; case TSBK_OSP_MOT_PSH_CCH: tsbkValue = 0U; break; // because of how MFId is handled; we have to skip these opcodes case TSBK_IOSP_UU_VCH: case TSBK_IOSP_STS_UPDT: case TSBK_IOSP_STS_Q: case TSBK_IOSP_MSG_UPDT: case TSBK_IOSP_CALL_ALRT: case TSBK_IOSP_GRP_AFF: case TSBK_IOSP_ACK_RSP: case TSBK_IOSP_U_REG: case TSBK_OSP_DENY_RSP: case TSBK_OSP_QUE_RSP: case TSBK_OSP_GRP_AFF_Q: case TSBK_OSP_U_REG_CMD: case TSBK_OSP_U_DEREG_ACK: break; default: LogError(LOG_P25, "TSBK::encode(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); break; } } // internal P25 vendor opcodes if (m_mfId == P25_MFG_DVM) { switch (m_lco) { case TSBK_OSP_DVM_GIT_HASH: tsbkValue = g_gitHashBytes[0]; // ... tsbkValue = (tsbkValue << 8) + (g_gitHashBytes[1U]); // ... tsbkValue = (tsbkValue << 8) + (g_gitHashBytes[2U]); // ... tsbkValue = (tsbkValue << 8) + (g_gitHashBytes[3U]); // ... tsbkValue = (tsbkValue << 16) + 0U; tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number break; case LC_CALL_TERM: tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; default: LogError(LOG_P25, "TSBK::encode(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); break; } } } // split rs value into bytes tsbk[2U] = (uint8_t)((tsbkValue >> 56) & 0xFFU); tsbk[3U] = (uint8_t)((tsbkValue >> 48) & 0xFFU); tsbk[4U] = (uint8_t)((tsbkValue >> 40) & 0xFFU); tsbk[5U] = (uint8_t)((tsbkValue >> 32) & 0xFFU); tsbk[6U] = (uint8_t)((tsbkValue >> 24) & 0xFFU); tsbk[7U] = (uint8_t)((tsbkValue >> 16) & 0xFFU); tsbk[8U] = (uint8_t)((tsbkValue >> 8) & 0xFFU); tsbk[9U] = (uint8_t)((tsbkValue >> 0) & 0xFFU); // compute CRC-CCITT 16 edac::CRC::addCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); if (m_verbose) { Utils::dump(2U, "TSBK::encode(), TSBK Value", tsbk, P25_TSBK_LENGTH_BYTES); } uint8_t raw[P25_TSBK_FEC_LENGTH_BYTES]; ::memset(raw, 0x00U, P25_TSBK_FEC_LENGTH_BYTES); // encode 1/2 rate Trellis m_trellis.encode12(tsbk, raw); // are we encoding a raw TSBK? if (rawTSBK) { if (noTrellis) { ::memcpy(data, tsbk, P25_TSBK_LENGTH_BYTES); } else { ::memcpy(data, raw, P25_TSBK_FEC_LENGTH_BYTES); } } else { // interleave P25Utils::encode(raw, data, 114U, 318U); #if DEBUG_P25_TSBK Utils::dump(2U, "TSBK::encode(), TSBK Interleave", data, P25_TSBK_FEC_LENGTH_BYTES + P25_PREAMBLE_LENGTH_BYTES); #endif } } /// /// Sets the flag to skip vendor opcode processing. /// /// Flag indicating to skip vendor opcode processing. void TSBK::setVendorSkip(bool skip) { m_vendorSkip = skip; } /// /// Sets the callsign. /// /// Callsign. void TSBK::setCallsign(std::string callsign) { uint32_t idLength = callsign.length(); if (idLength > 0) { ::memset(m_siteCallsign, 0x20U, P25_MOT_CALLSIGN_LENGTH_BYTES); if (idLength > P25_MOT_CALLSIGN_LENGTH_BYTES) idLength = P25_MOT_CALLSIGN_LENGTH_BYTES; for (uint32_t i = 0; i < idLength; i++) m_siteCallsign[i] = callsign[i]; } } /** Authentication data */ /// Gets the authentication result. /// void TSBK::getAuthRes(uint8_t* res) const { assert(res != NULL); ::memcpy(res, m_authRes, P25_AUTH_RES_LENGTH_BYTES); } /// Sets the authentication random seed. /// void TSBK::setAuthRS(const uint8_t* rs) { assert(rs != NULL); ::memcpy(m_authRS, rs, P25_AUTH_RAND_SEED_LENGTH_BYTES); } /// Gets the authentication random seed. /// void TSBK::getAuthRS(uint8_t* rs) const { assert(rs != NULL); ::memcpy(rs, m_authRS, P25_AUTH_RAND_SEED_LENGTH_BYTES); } /// Sets the authentication random challenge. /// void TSBK::setAuthRC(const uint8_t* rc) { assert(rc != NULL); ::memcpy(m_authRC, rc, P25_AUTH_RAND_CHLNG_LENGTH_BYTES); } /// Gets the authentication random challenge. /// void TSBK::getAuthRC(uint8_t* rc) const { assert(rc != NULL); ::memcpy(rc, m_authRC, P25_AUTH_RAND_CHLNG_LENGTH_BYTES); } // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the TSBK class. /// /// This should never be used. TSBK::TSBK() : TSBK(SiteData()) { /* stub */ } /// /// Initializes a new instance of the TSBK class. /// /// TSBK::TSBK(SiteData siteData) : m_verbose(false), #if FORCE_TSBK_CRC_WARN m_warnCRC(true), #else m_warnCRC(false), #endif m_protect(false), m_lco(LC_GROUP), m_mfId(P25_MFG_STANDARD), m_srcId(0U), m_dstId(0U), m_lastBlock(false), m_aivFlag(true), m_extendedAddrFlag(false), m_service(0U), m_response(P25_RSP_ACCEPT), m_netId(P25_WACN_STD_DEFAULT), m_sysId(P25_SID_STD_DEFAULT), m_grpVchId(0U), m_grpVchNo(0U), m_messageValue(0U), m_statusValue(0U), m_extendedFunction(P25_EXT_FNCT_CHECK), m_microslotCount(0U), m_dataServiceOptions(0U), m_dataAccessControl(0U), m_dataChannelNo(0U), m_adjCFVA(P25_CFVA_FAILURE), m_adjRfssId(0U), m_adjSiteId(0U), m_adjChannelId(0U), m_adjChannelNo(0U), m_adjServiceClass(P25_SVC_CLS_INVALID), m_sccbChannelId1(0U), m_sccbChannelId2(0U), m_sccbChannelNo(0U), m_lra(0U), m_patchSuperGroupId(0U), m_patchGroup1Id(0U), m_patchGroup2Id(0U), m_patchGroup3Id(0U), m_emergency(false), m_encrypted(false), m_priority(4U), m_group(true), m_txMult(0U), m_authSuccess(false), m_authStandalone(false), m_siteData(siteData), m_siteIdenEntry(), m_rs(), m_trellis(), m_vendorSkip(false), m_sndcpAutoAccess(true), m_sndcpReqAccess(false), m_sndcpDAC(1U), m_authRes(NULL), m_authRS(NULL), m_authRC(NULL), m_siteCallsign(NULL) { m_siteCallsign = new uint8_t[P25_MOT_CALLSIGN_LENGTH_BYTES]; ::memset(m_siteCallsign, 0x00U, P25_MOT_CALLSIGN_LENGTH_BYTES); setCallsign(siteData.callsign()); m_authRes = new uint8_t[P25_AUTH_RES_LENGTH_BYTES]; ::memset(m_authRes, 0x00U, P25_AUTH_RES_LENGTH_BYTES); m_authRS = new uint8_t[P25_AUTH_RAND_SEED_LENGTH_BYTES]; ::memset(m_authRS, 0x00U, P25_AUTH_RAND_SEED_LENGTH_BYTES); m_authRC = new uint8_t[P25_AUTH_RAND_CHLNG_LENGTH_BYTES]; ::memset(m_authRC, 0x00U, P25_AUTH_RAND_CHLNG_LENGTH_BYTES); } /// /// Internal helper to copy the the class. /// /// void TSBK::copy(const TSBK& data) { m_verbose = data.m_verbose; m_warnCRC = data.m_warnCRC; m_protect = data.m_protect; m_lco = data.m_lco; m_mfId = data.m_mfId; m_srcId = data.m_srcId; m_dstId = data.m_dstId; m_lastBlock = data.m_lastBlock; m_aivFlag = data.m_aivFlag; m_extendedAddrFlag = data.m_extendedAddrFlag; m_service = data.m_service; m_response = data.m_response; m_netId = data.m_netId; m_sysId = data.m_sysId; m_grpVchNo = data.m_grpVchNo; m_messageValue = data.m_messageValue; m_statusValue = data.m_statusValue; m_extendedFunction = data.m_extendedFunction; m_microslotCount = data.m_microslotCount; m_dataChannelNo = data.m_dataChannelNo; m_adjCFVA = data.m_adjCFVA; m_adjRfssId = data.m_adjRfssId; m_adjSiteId = data.m_adjSiteId; m_adjChannelId = data.m_adjChannelId; m_adjChannelNo = data.m_adjChannelNo; m_adjServiceClass = data.m_adjServiceClass; m_sccbChannelId1 = data.m_sccbChannelId1; m_sccbChannelId2 = data.m_sccbChannelId2; m_sccbChannelNo = data.m_sccbChannelNo; m_lra = data.m_lra; m_patchSuperGroupId = data.m_patchSuperGroupId; m_patchGroup1Id = data.m_patchGroup1Id; m_patchGroup2Id = data.m_patchGroup2Id; m_patchGroup3Id = data.m_patchGroup3Id; m_emergency = data.m_emergency; m_encrypted = data.m_encrypted; m_priority = data.m_priority; m_group = data.m_group; m_txMult = data.m_txMult; m_authSuccess = data.m_authSuccess; m_authStandalone = data.m_authStandalone; m_siteData = data.m_siteData; m_siteIdenEntry = data.m_siteIdenEntry; if (m_authRes != NULL) { delete[] m_authRes; } m_authRes = new uint8_t[P25_AUTH_RES_LENGTH_BYTES]; ::memcpy(m_authRes, data.m_authRes, P25_AUTH_RES_LENGTH_BYTES); if (m_authRS != NULL) { delete[] m_authRes; } m_authRS = new uint8_t[P25_AUTH_RAND_SEED_LENGTH_BYTES]; ::memcpy(m_authRS, data.m_authRS, P25_AUTH_RAND_SEED_LENGTH_BYTES); if (m_authRC != NULL) { delete[] m_authRC; } m_authRC = new uint8_t[P25_AUTH_RAND_CHLNG_LENGTH_BYTES]; ::memcpy(m_authRC, data.m_authRC, P25_AUTH_RAND_CHLNG_LENGTH_BYTES); delete[] m_siteCallsign; uint8_t* callsign = new uint8_t[P25_MOT_CALLSIGN_LENGTH_BYTES]; ::memcpy(callsign, data.m_siteCallsign, P25_MOT_CALLSIGN_LENGTH_BYTES); m_siteCallsign = callsign; }