From f3698cbe86ca7881a26e52246ba74a28646afb03 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 15 Jul 2026 15:48:32 -0400 Subject: [PATCH] encode the raw CSBK/TSBK/RCCH data in the control net event data; decode and send net_event for NXDN RCCH data; --- src/common/nxdn/lc/RCCH.cpp | 20 +++++++-- src/common/nxdn/lc/RCCH.h | 12 +++++- src/sysview/SysViewMain.cpp | 81 +++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/src/common/nxdn/lc/RCCH.cpp b/src/common/nxdn/lc/RCCH.cpp index 38727e3b..d77a8888 100644 --- a/src/common/nxdn/lc/RCCH.cpp +++ b/src/common/nxdn/lc/RCCH.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2022,2024,2026 Bryan Biedenkapp, N2PLL * */ #include "nxdn/NXDNDefines.h" @@ -49,7 +49,8 @@ RCCH::RCCH() : m_group(true), m_duplex(false), m_transmissionMode(TransmissionMode::MODE_4800), - m_siteIdenEntry() + m_siteIdenEntry(), + m_raw(nullptr) { if (s_siteCallsign == nullptr) { s_siteCallsign = new uint8_t[CALLSIGN_LENGTH_BYTES]; @@ -68,7 +69,8 @@ RCCH::RCCH(const RCCH& data) : RCCH() RCCH::~RCCH() { - /* stub */ + if (m_raw != nullptr) + delete[] m_raw; } /* Returns a string that represents the current RCCH. */ @@ -78,6 +80,13 @@ std::string RCCH::toString(bool isp) return std::string("MESSAGE_TYPE_UNKWN (Unknown RCCH)"); } +/* Returns a copy of the raw decoded RCCH bytes. */ + +uint8_t* RCCH::getDecodedRaw() const +{ + return m_raw; +} + /* Sets the callsign. */ void RCCH::setCallsign(std::string callsign) @@ -118,6 +127,11 @@ void RCCH::decode(const uint8_t* data, uint8_t* rcch, uint32_t length, uint32_t Utils::dump(2U, "NXDN, RCCH::decode(), Decoded RCCH Data", rcch, NXDN_RCCH_LC_LENGTH_BYTES); } + if (m_raw != nullptr) + delete[] m_raw; + m_raw = new uint8_t[NXDN_RCCH_LC_LENGTH_BYTES]; + ::memcpy(m_raw, rcch, NXDN_RCCH_LC_LENGTH_BYTES); + m_messageType = data[0U] & 0x3FU; // Message Type } diff --git a/src/common/nxdn/lc/RCCH.h b/src/common/nxdn/lc/RCCH.h index b497397a..0c2cad8f 100644 --- a/src/common/nxdn/lc/RCCH.h +++ b/src/common/nxdn/lc/RCCH.h @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2022 Bryan Biedenkapp, N2PLL + * Copyright (C) 2022,2026 Bryan Biedenkapp, N2PLL * */ /** @@ -69,6 +69,13 @@ namespace nxdn */ virtual std::string toString(bool isp = false); + /** + * @brief Returns a copy of the raw decoded RCCH bytes. + * This will only return data for a *decoded* RCCH, not a created or copied RCCH. + * @returns uint8_t* Raw decoded RCCH bytes. + */ + uint8_t* getDecodedRaw() const; + /** * @brief Gets the flag indicating verbose log output. * @returns bool True, if the RCCH is verbose logging, otherwise false. @@ -207,6 +214,9 @@ namespace nxdn void encode(uint8_t* data, const uint8_t* rcch, uint32_t length, uint32_t offset = 0U); DECLARE_PROTECTED_COPY(RCCH); + + private: + uint8_t* m_raw; }; } // namespace lc } // namespace nxdn diff --git a/src/sysview/SysViewMain.cpp b/src/sysview/SysViewMain.cpp index ae701e0d..39192f5e 100644 --- a/src/sysview/SysViewMain.cpp +++ b/src/sysview/SysViewMain.cpp @@ -20,7 +20,11 @@ #include "common/p25/lc/tdulc/TDULCFactory.h" #include "common/p25/lc/tsbk/TSBKFactory.h" #include "common/nxdn/NXDNDefines.h" +#include "common/nxdn/channel/LICH.h" +#include "common/nxdn/lc/RCCH.h" +#include "common/nxdn/lc/rcch/RCCHFactory.h" #include "common/nxdn/lc/RTCH.h" +#include "common/nxdn/NXDNUtils.h" #include "common/yaml/Yaml.h" #include "common/Log.h" #include "common/StopWatch.h" @@ -485,6 +489,15 @@ void* threadNetworkPump(void* arg) std::string desc = csbk->toString(); netEvent["desc"].set(desc); + // report raw CSBK bytes in hex format + uint8_t* csbkRaw = csbk.get()->getDecodedRaw(); + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (uint8_t i = 0; i < DMRDEF::DMR_CSBK_LENGTH_BYTES; i++) { + ss << std::setw(2) << (int)csbkRaw[i]; + } + netEvent["data"].set(ss.str()); + switch (csbk->getCSBKO()) { case DMRDEF::CSBKO::BROADCAST: { @@ -648,6 +661,15 @@ void* threadNetworkPump(void* arg) std::string desc = tsbk->toString(); netEvent["desc"].set(desc); + // report raw TSBK bytes in hex format + uint8_t* tsbkRaw = tsbk.get()->getDecodedRaw(); + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (uint8_t i = 0; i < P25DEF::P25_TSBK_LENGTH_BYTES; i++) { + ss << std::setw(2) << (int)tsbkRaw[i]; + } + netEvent["data"].set(ss.str()); + switch (tsbk->getLCO()) { case P25DEF::TSBKO::IOSP_GRP_VCH: case P25DEF::TSBKO::IOSP_UU_VCH: @@ -1010,6 +1032,24 @@ void* threadNetworkPump(void* arg) bool group = (nxdnBuffer[15U] & 0x40U) == 0x40U ? false : true; lc.setGroup(group); + // process raw NXDN data bytes + UInt8Array frame; + uint8_t frameLength = nxdnBuffer[23U]; + if (frameLength <= 24) { + frame = std::unique_ptr(new uint8_t[frameLength]); + ::memset(frame.get(), 0x00U, frameLength); + } + else { + frame = std::unique_ptr(new uint8_t[frameLength]); + ::memset(frame.get(), 0x00U, frameLength); + ::memcpy(frame.get(), nxdnBuffer.get() + 24U, frameLength); + } + + NXDNUtils::scrambler(frame.get() + 2U); + + channel::LICH lich; + bool valid = lich.decode(frame.get() + 2U); + // specifically only check the following logic for end of call, voice or data frames if ((messageType == NXDDEF::MessageType::RTCH_TX_REL || messageType == NXDDEF::MessageType::RTCH_TX_REL_EX) || (messageType == NXDDEF::MessageType::RTCH_VCALL || messageType == NXDDEF::MessageType::RTCH_DCALL_HDR || @@ -1057,6 +1097,47 @@ void* threadNetworkPump(void* arg) } } + std::unique_ptr rcch; + if (valid) { + NXDDEF::RFChannelType::E rfct = lich.getRFCT(); + NXDDEF::FuncChannelType::E fct = lich.getFCT(); + NXDDEF::ChOption::E option = lich.getOption(); + + if (rfct == NXDDEF::RFChannelType::RCCH) { + rcch = lc::rcch::RCCHFactory::createRCCH(frame.get(), frameLength); + + json::object netEvent = json::object(); + std::string type = "rcch"; + netEvent["type"].set(type); + + uint8_t lco = rcch->getMessageType(); + netEvent["opcode"].set(lco); + std::string desc = rcch->toString(); + netEvent["desc"].set(desc); + + // report raw RCCH bytes in hex format + uint8_t* rcchRaw = rcch.get()->getDecodedRaw(); + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (uint8_t i = 0; i < NXDDEF::NXDN_RCCH_LC_LENGTH_BYTES; i++) { + ss << std::setw(2) << (int)rcchRaw[i]; + } + netEvent["data"].set(ss.str()); + + // generate a net event for this + if (g_netDataEvent != nullptr) { + std::string resolvedSrc = resolveRID(srcId); + std::string resolvedDst = resolveTGID(dstId); + netEvent["srcId"].set(srcId); + netEvent["srcStr"].set(resolvedSrc); + netEvent["dstId"].set(dstId); + netEvent["dstStr"].set(resolvedDst); + + g_netDataEvent(netEvent); + } + } + } + if (g_debug) LogInfoEx(LOG_NET, "NXDN, messageType = $%02X, srcId = %u, dstId = %u, len = %u", messageType, srcId, dstId, length); }