// SPDX-License-Identifier: GPL-2.0-only /* * Digital Voice Modem - Common Library * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX * Copyright (C) 2020-2026 Bryan Biedenkapp, N2PLL * */ /** * @defgroup network_core Networking Core * @brief Implementation for the core networking. * @ingroup common * @defgroup socket Sockets * @brief Implementation for handling network sockets. * @ingroup network_core * * @file BaseNetwork.h * @ingroup network_core * @file BaseNetwork.cpp * @ingroup network_core */ #if !defined(__BASE_NETWORK_H__) #define __BASE_NETWORK_H__ #include "common/Defines.h" #include "common/analog/data/NetData.h" #include "common/dmr/data/NetData.h" #include "common/p25/data/DataHeader.h" #include "common/p25/data/LowSpeedData.h" #include "common/p25/lc/LC.h" #include "common/p25/Audio.h" #include "common/nxdn/lc/RTCH.h" #include "common/json/json.h" #include "common/network/FrameQueue.h" #include "common/network/udp/Socket.h" #include "common/RingBuffer.h" #include "common/Utils.h" #include #include #include #include // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- #define DVM_RAND_MIN 0x00000001 #define DVM_RAND_MAX 0xfffffffe #define TAG_DMR_DATA "DMRD" #define TAG_P25_DATA "P25D" #define TAG_NXDN_DATA "NXDD" #define TAG_ANALOG_DATA "ANOD" #define TAG_REPEATER_LOGIN "RPTL" #define TAG_REPEATER_AUTH "RPTK" #define TAG_REPEATER_CONFIG "RPTC" #define TAG_REPEATER_PING "RPTP" #define TAG_REPEATER_GRANT "RPTG" #define TAG_REPEATER_KEY "RKEY" #define TAG_INCALL_CTRL "ICC " #define TAG_TRANSFER "TRNS" #define TAG_TRANSFER_ACT_LOG "TRNSLOG" #define TAG_TRANSFER_DIAG_LOG "TRNSDIAG" #define TAG_TRANSFER_STATUS "TRNSSTS" #define TAG_TRANSFER_PATCH_STATUS "TRNSPTCH" #define TAG_ANNOUNCE "ANNC" #define TAG_PEER_REPLICA "REPL" #define MAX_PEER_PING_TIME 60U // 60 seconds namespace network { // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- const uint32_t PACKET_PAD = 8U; const uint32_t MSG_HDR_SIZE = 24U; const uint32_t MSG_ANNC_GRP_AFFIL = 6U; const uint32_t MSG_ANNC_GRP_UNAFFIL = 3U; const uint32_t MSG_ANNC_UNIT_REG = 3U; const uint32_t DMR_PACKET_LENGTH = 55U; // 20 byte header + DMR_FRAME_LENGTH_BYTES + 2 byte trailer const uint32_t P25_LDU1_PACKET_LENGTH = 193U; // 24 byte header + DFSI data + 1 byte frame type + 12 byte enc sync const uint32_t P25_LDU2_PACKET_LENGTH = 181U; // 24 byte header + DFSI data + 1 byte frame type const uint32_t P25_TSDU_PACKET_LENGTH = 69U; // 24 byte header + TSDU data const uint32_t P25_TDULC_PACKET_LENGTH = 78U; // 24 byte header + TDULC data const uint32_t P25_P2_PACKET_LENGTH = 66U; // 24 byte header + P25_P2_FRAME_LENGTH_BYTES + 2 byte trailer const uint32_t NXDN_PACKET_LENGTH = 70U; // 20 byte header + NXDN_FRAME_LENGTH_BYTES + 2 byte trailer const uint32_t ANALOG_PACKET_LENGTH = 344U; // 20 byte header + AUDIO_SAMPLES_LENGTH_BYTES + 4 byte trailer const uint32_t HA_PARAMS_ENTRY_LEN = 20U; /** * @brief Network Peer Connection Status * @ingroup network_core */ enum NET_CONN_STATUS { // Common States NET_STAT_WAITING_CONNECT, //!< Waiting for Connection NET_STAT_WAITING_LOGIN, //!< Waiting for Login NET_STAT_WAITING_AUTHORISATION, //!< Waiting for Authorization NET_STAT_WAITING_CONFIG, //!< Waiting for Configuration NET_STAT_RUNNING, //!< Peer Running // Master States NET_STAT_RPTL_RECEIVED, //!< Login Received NET_STAT_CHALLENGE_SENT, //!< Authentication Challenge Sent NET_STAT_MST_RUNNING, //!< Master Running NET_STAT_INVALID = 0x7FFFFFF //!< Invalid }; /** * @brief Network Peer NAK Reasons * @ingroup network_core */ enum NET_CONN_NAK_REASON { NET_CONN_NAK_GENERAL_FAILURE, //!< General Failure NET_CONN_NAK_MODE_NOT_ENABLED, //!< Mode Not Enabled NET_CONN_NAK_ILLEGAL_PACKET, //!< Illegal Packet NET_CONN_NAK_FNE_UNAUTHORIZED, //!< FNE Unauthorized NET_CONN_NAK_BAD_CONN_STATE, //!< Bad Connection State NET_CONN_NAK_INVALID_CONFIG_DATA, //!< Invalid Configuration Data NET_CONN_NAK_PEER_RESET, //!< Peer Reset NET_CONN_NAK_PEER_ACL, //!< Peer ACL NET_CONN_NAK_FNE_MAX_CONN, //!< FNE Maximum Connections NET_CONN_NAK_FNE_DUPLICATE_CONN, //!< FNE Duplicate Connection NET_CONN_NAK_INVALID = 0xFFFF //!< Invalid }; /** * @brief Network Control Enumerations * @note These values are used typically for terminators to specify specific DVM in-band control operations. * @ingroup network_core */ enum CONTROL_BYTE { NET_CTRL_GRANT_DEMAND = 0x80U, //!< Grant Demand NET_CTRL_GRANT_DENIAL = 0x40U, //!< Grant Denial NET_CTRL_SWITCH_OVER = 0x20U, //!< Call Source RID Switch Over NET_CTRL_GRANT_ENCRYPT = 0x08U, //!< Grant Encrypt NET_CTRL_U2U = 0x01U, //!< Unit-to-Unit }; /** * @brief Peer Connection Class Enumerations * @note These values define which connection class a peer connection belongs to. This is used for determining how * to handle traffic from the peer. * @ingroup network_core */ enum PEER_CONN_CLASS { PEER_CONN_CLASS_UNKNOWN, //!< Unknown PEER_CONN_CLASS_NEIGHBOR, //!< Neighbor FNE Peer PEER_CONN_CLASS_STANDARD, //!< Standard Peer PEER_CONN_CLASS_SYSVIEW, //!< SysView Peer PEER_CONN_CLASS_CONSOLE, //!< Console Peer // this should always be last PEER_CONN_CLASS_INVALID }; /** * @brief RTP Stream Multiplex Validation Return Codes * @ingroup network_core */ enum MULTIPLEX_RET_CODE { MUX_VALID_SUCCESS = 0U, //!< Successful Validation MUX_LOST_FRAMES = 1U, //!< Lost Frames MUX_OUT_OF_ORDER = 2U //!< Out-of-Order }; // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- /** * @brief Handles dealing with maintaining RTP sequencing for multiple multiplexed RTP streams. * @ingroup fne_network */ class HOST_SW_API RTPStreamMultiplex { public: auto operator=(RTPStreamMultiplex&) -> RTPStreamMultiplex& = delete; auto operator=(RTPStreamMultiplex&&) -> RTPStreamMultiplex& = delete; RTPStreamMultiplex(RTPStreamMultiplex&) = delete; /** * @brief Initializes a new instance of the RTPStreamMultiplex class. */ RTPStreamMultiplex() : m_mutex(), m_streamSeqNos() { /* stub */ } /** * @brief Finalizes a instance of the RTPStreamMultiplex class. */ ~RTPStreamMultiplex() { m_streamSeqNos.clear(); } /** * @brief Helper to verify the given RTP sequence for the given multiplexed RTP stream. * @param streamId Stream ID. * @param pktSeq Packet Sequence. * @param func Network function. * @param[out] lastRxSeq Last Received Sequence. * @return MULTIPLEX_RET_CODE Return code. */ MULTIPLEX_RET_CODE verifyStream(uint64_t streamId, uint16_t pktSeq, uint8_t func, uint16_t* lastRxSeq) { MULTIPLEX_RET_CODE ret = MUX_VALID_SUCCESS; if (pktSeq == RTP_END_OF_CALL_SEQ) { // only reset packet sequences if we're a PROTOCOL or RPTC function if ((func == NET_FUNC::PROTOCOL) || (func == NET_FUNC::RPTC)) { erasePktSeq(streamId); // attempt to erase packet sequence for the stream } } else { if (hasPktSeq(streamId)) { *lastRxSeq = getPktSeq(streamId); if (*lastRxSeq == RTP_END_OF_CALL_SEQ) { // reset the received sequence back to 0 setPktSeq(streamId, 0U); } else { if ((pktSeq >= *lastRxSeq) || (pktSeq == 0U)) { // if the sequence isn't 0, and is greater then the last received sequence + 1 frame // assume a packet was lost if ((pktSeq != 0U) && pktSeq >= *lastRxSeq + 1U) { ret = MUX_LOST_FRAMES; } setPktSeq(streamId, pktSeq); } else { if (pktSeq < *lastRxSeq) { ret = MUX_OUT_OF_ORDER; } } } } } return ret; } /** * @brief Helper to return the current count of multiplexed RTP streams. * @returns size_t Count of stored streams. */ size_t streamCount() { return m_streamSeqNos.size(); } /** * @brief Helper to determine if the given multiplexed stream has a stored RTP sequence. * @param streamId Stream ID. * @returns bool True, if stream ID has a stored RTP sequence, otherwise false. */ bool hasPktSeq(uint64_t streamId) { bool ret = false; std::lock_guard lock(m_mutex); // determine if the stream has a current sequence no and return { auto it = m_streamSeqNos.find(streamId); if (it == m_streamSeqNos.end()) { ret = false; } else { ret = true; } } return ret; } /** * @brief Helper to get the stored RTP sequence for the given multiplexed stream. * @param streamId Stream ID. * @returns uint16_t Sequence number. */ uint16_t getPktSeq(uint64_t streamId) { std::lock_guard lock(m_mutex); // find the current sequence no and return uint32_t pktSeq = 0U; { auto it = m_streamSeqNos.find(streamId); if (it == m_streamSeqNos.end()) { pktSeq = RTP_END_OF_CALL_SEQ; } else { pktSeq = m_streamSeqNos[streamId]; } } return pktSeq; } /** * @brief Helper to set the stored RTP sequence for the given multiplexed stream. * @param streamId Stream ID. * @param seq Sequence number. */ void setPktSeq(uint64_t streamId, uint16_t seq) { std::lock_guard lock(m_mutex); auto it = m_streamSeqNos.find(streamId); if (it == m_streamSeqNos.end()) { m_streamSeqNos.insert({streamId, seq}); } else { m_streamSeqNos[streamId] = seq; } } /** * @brief Helper to increment the stored RTP sequence for the given multiplexed stream. * @param streamId Stream ID. * @returns uint16_t Incremented packet sequence. */ uint16_t incPktSeq(uint64_t streamId) { std::lock_guard lock(m_mutex); uint32_t pktSeq = 0U; auto it = m_streamSeqNos.find(streamId); if (it == m_streamSeqNos.end()) { m_streamSeqNos.insert({streamId, pktSeq}); } else { pktSeq = m_streamSeqNos[streamId]; ++pktSeq; if (pktSeq > (RTP_END_OF_CALL_SEQ - 1U)) pktSeq = 0U; m_streamSeqNos[streamId] = pktSeq; } return pktSeq; } /** * @brief Helper to erase the stored RTP sequence for the given multiplexed stream. * @param streamId Stream ID. */ void erasePktSeq(uint64_t streamId) { std::lock_guard lock(m_mutex); // find the sequence no and erase { auto entry = m_streamSeqNos.find(streamId); if (entry != m_streamSeqNos.end()) { m_streamSeqNos.erase(streamId); } } } private: std::recursive_mutex m_mutex; std::unordered_map m_streamSeqNos; }; // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- /** * @brief Implements the base networking logic. * @ingroup network_core */ class HOST_SW_API BaseNetwork { public: /** * @brief Initializes a new instance of the BaseNetwork class. * @param peerId Unique ID of this modem on the network. * @param duplex Flag indicating full-duplex operation. * @param debug Flag indicating whether network debug is enabled. * @param slot1 Flag indicating whether DMR/P25 Phase 2 slot 1 is enabled for network traffic. * @param slot2 Flag indicating whether DMR/P25 Phase 2 slot 2 is enabled for network traffic. * @param allowActivityTransfer Flag indicating that the system activity logs will be sent to the network. * @param allowDiagnosticTransfer Flag indicating that the system diagnostic logs will be sent to the network. * @param localPort Local port used to listen for incoming data. */ BaseNetwork(uint32_t peerId, bool duplex, bool debug, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, uint16_t localPort = 0U); /** * @brief Finalizes a instance of the BaseNetwork class. */ virtual ~BaseNetwork(); /** * @brief Gets the frame queue for the network. */ FrameQueue* getFrameQueue() const { return m_frameQueue; } /** * @brief Helper to enable or disable packet dump logging. * @param enable Flag indicating whether packet dump logging is enabled. */ void setPacketDump(bool enable) { m_packetDump = enable; if (m_frameQueue != nullptr) m_frameQueue->setDebug(enable); } /** * @brief Writes a grant request to the network. * \code{.unparsed} * Below is the representation of the data layout for the group affiliation * announcement message. The message is 24 bytes in length. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Source ID | Destination ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Destination ID | Reserved |S| Reserverd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Mode | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param mode Digital Mode. * @param srcId Source Radio ID. * @param dstId Destination Radio ID. * @param slot DMR slot number (if DMR). * @param unitToUnit Flag indicating if this grant is unit-to-unit. * @returns bool True, if grant request was sent, otherwise false. */ bool writeGrantReq(const uint8_t mode, const uint32_t srcId, const uint32_t dstId, const uint8_t slot, const bool unitToUnit); /** * @brief Writes a enc. key request to the network. * @param kId Key ID. * @param algId Algorithm ID. * @param srcId Source Radio ID. * @returns bool True, if request was sent, otherwise false. */ bool writeKeyReq(const uint16_t kId, const uint8_t algId, const uint32_t srcId = 0U); /** * @brief Writes a LLA enc. key request to the network. * @param srcId Source Radio ID. * @returns bool True, if request was sent, otherwise false. */ bool writeLLAKeyReq(const uint32_t srcId); /** * @brief Writes the local activity log to the network. * \code{.unparsed} * Below is the representation of the data layout for the activity log message. * The message is variable length bytes. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | Log Message . | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Log Message ................................................. | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param message Textual string to send as activity log information. * @returns bool True, if message was sent, otherwise false. */ virtual bool writeActLog(const char* message); /** * @brief Writes the local diagnostic logs to the network. * \code{.unparsed} * Below is the representation of the data layout for the diagnostic log message. * The message is variable length bytes. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | Log Message . | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Log Message ................................................. | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @returns bool True, if message was sent, otherwise false. */ virtual bool writeDiagLog(const char* message); /** * @brief Writes the local status to the network. * \code{.unparsed} * Below is the representation of the data layout for the peer status message. * The message is variable length bytes. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | Variable | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Length JSON Payload ......................................... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The JSON payload is variable length and looks like this: * { * "state": , * "isTxCW": , * "fixedMode": , * "dmrTSCCEnable": , * "dmrCC": , * "p25CtrlEnable": , * "p25CC": , * "nxdnCtrlEnable": , * "nxdnCC": , * "tx": , * "channelId": , * "channelNo": , * "lastDstId": , * "lastSrcId": , * "peerId": , * "sysId": , * "siteId": , * "p25RfssId": , * "p25NetId": , * "p25NAC": , * "vcChannels": [ * { * "channelId": , * "channelNo": , * "tx": , * "lastDstId": , * "lastSrcId": , * } * ], * "modem": { * "portType": "", * "modemPort": "", * "portSpeed": , * "rxLevel": , * "cwTxLevel": , * "dmrTxLevel": , * "p25TxLevel": , * "nxdnTxLevel": , * "rxDCOffset": , * "txDCOffset": , * "fdmaPremables": , * "dmrRxDelay": , * "p25CorrCount": , * "rxFrequency": , * "txFrequency": , * "rxTuning": , * "txTuning": , * "rxFrequencyEffective": , * "txFrequencyEffective": , * "v24Connected": , * "protoVer": * } * } * \endcode * @param obj JSON object representing the local peer status. * @returns bool True, if peer status was sent, otherwise false. */ virtual bool writePeerStatus(json::object obj); /** * @brief Writes a group affiliation to the network. * \code{.unparsed} * Below is the representation of the data layout for the group affiliation * announcement message. The message is 6 bytes in length. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Source ID | Dest ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param srcId Source Radio ID. * @param dstId Destination Talkgroup ID. * @returns bool True, if group affiliation announcement was sent, otherwise false. */ virtual bool announceGroupAffiliation(uint32_t srcId, uint32_t dstId); /** * @brief Writes a group affiliation removal to the network. * \code{.unparsed} * Below is the representation of the data layout for the unit registration * announcement message. The message is 3 bytes in length. * * Byte 0 1 2 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param srcId Source Radio ID. * @returns bool True, if group affiliation announcement was sent, otherwise false. */ virtual bool announceGroupAffiliationRemoval(uint32_t srcId); /** * @brief Writes a unit registration to the network. * \code{.unparsed} * Below is the representation of the data layout for the unit registration * announcement message. The message is 3 bytes in length. * * Byte 0 1 2 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param srcId Source Radio ID. * @returns bool True, if unit registration announcement was sent, otherwise false. */ virtual bool announceUnitRegistration(uint32_t srcId); /** * @brief Writes a unit deregistration to the network. * \code{.unparsed} * Below is the representation of the data layout for the unit deregistration * announcement message. The message is 3 bytes in length. * * Byte 0 1 2 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param srcId Source Radio ID. * @returns bool True, if unit deregistration announcement was sent, otherwise false. */ virtual bool announceUnitDeregistration(uint32_t srcId); /** * @brief Writes a complete update of the peer affiliation list to the network. * \code{.unparsed} * Below is the representation of the data layout for the repeater/end point login message. * The message is variable bytes in length. * * Each affiliation update entry is 7 bytes. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Number of entries | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Entry: Source ID | E: Dst Id | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Entry: Destination ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param affs Complete map of peer unit affiliations. * @returns bool True, if affiliation update announcement was sent, otherwise false. */ virtual bool announceAffiliationUpdate(const std::unordered_map affs); /** * @brief Writes a complete update of the peer's unit registration list to the network. * \code{.unparsed} * Below is the representation of the data layout for the repeater/end point login message. * The message is variable bytes in length. * * Each unit registration update entry is 3 bytes. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Number of entries | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Entry: Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param regs Complete list of unit registrations. * @returns bool True, if unit registration update announcement was sent, otherwise false. */ virtual bool announceUnitRegUpdate(const std::vector regs); /** * @brief Writes a complete update of the peer's voice channel list to the network. * \code{.unparsed} * Below is the representation of the data layout for the repeater/end point login message. * The message is variable bytes in length. * * Each peer ID entry is 4 bytes. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Number of entries | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Entry: Peer ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param peers List of voice channel peers. * @returns bool True, if peer update announcement was sent, otherwise false. */ virtual bool announceSiteVCs(const std::vector peers); /** * @brief Updates the timer by the passed number of milliseconds. * @param ms Number of milliseconds. */ virtual void clock(uint32_t ms) = 0; /** * @brief Opens connection to the network. * @returns bool True, if connection to network opened, otherwise false. */ virtual bool open() = 0; /** * @brief Closes connection to the network. */ virtual void close() = 0; /** * @brief Resets the DMR ring buffer for the given slot. * @param slotNo DMR slot number. */ virtual void resetDMR(uint32_t slotNo); /** * @brief Resets the P25 ring buffer. */ virtual void resetP25(); /** * @brief Resets the P25 Phase 2 ring buffer for the given slot. * @param slotNo P25 Phase 2 slot number. */ virtual void resetP25P2(uint32_t slotNo); /** * @brief Resets the NXDN ring buffer. */ virtual void resetNXDN(); /** * @brief Resets the analog ring buffer. */ virtual void resetAnalog(); /** * @brief Gets the current DMR stream ID. * @param slotNo DMR slot to get stream ID for. * @return uint32_t Stream ID for the given DMR slot. */ uint32_t getDMRStreamId(uint32_t slotNo) const; /** * @brief Gets the current P25 stream ID. * @return uint32_t Stream ID. */ uint32_t getP25StreamId() const { return m_p25StreamId; } /** * @brief Gets the current P25 Phase 2 stream ID. * @param slotNo P25 Phase 2 slot to get stream ID for. * @return uint32_t Stream ID for the given P25 Phase 2 slot. */ uint32_t getP25P2StreamId(uint32_t slotNo) const; /** * @brief Gets the current NXDN stream ID. * @return uint32_t Stream ID. */ uint32_t getNXDNStreamId() const { return m_nxdnStreamId; } /** * @brief Gets the current analog stream ID. * @return uint32_t Stream ID. */ uint32_t getAnalogStreamId() const { return m_analogStreamId; } /** * @brief Helper to send a data message to the master. * @param opcode Opcode. * @param data Buffer to write to the network. * @param length Length of buffer to write. * @param pktSeq RTP packet sequence. * @param streamId Stream ID. * @param metadata Flag indicating the message should be sent to the metadata port. * @param peerId If non-zero, overrides the peer ID sent in the packet to the master. * @param ssrc If non-zero, overrides the RTP synchronization source ID sent in the packet to the master. * @returns bool True, if message was sent, otherwise false. */ bool writeMaster(FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, uint16_t pktSeq, uint32_t streamId, bool metadata = false, uint32_t peerId = 0U, uint32_t ssrc = 0U); // Digital Mobile Radio /** * @brief Reads DMR raw frame data from the DMR ring buffer. * @param[out] ret Flag indicating whether or not data was received. * @param[out] frameLength Length in bytes of received frame. * @returns UInt8Array Buffer containing received frame. */ virtual UInt8Array readDMR(bool& ret, uint32_t& frameLength); /** * @brief Writes DMR frame data to the network. * @param[in] data Instance of the dmr::data::NetData class containing the DMR message. * @param noSequence Flag indicating the message should be sent with no RTP sequence (65535). * @returns bool True, if message was sent, otherwise false. */ virtual bool writeDMR(const dmr::data::NetData& data, bool noSequence = false); /** * @brief Helper to test if the DMR ring buffer has data. * @returns bool True, if the network DMR ring buffer has data, otherwise false. */ bool hasDMRData() const; // Project 25 /** * @brief Reads P25 raw frame data from the P25 ring buffer. * @param[out] ret Flag indicating whether or not data was received. * @param[out] frameLength Length in bytes of received frame. * @returns UInt8Array Buffer containing received frame. */ virtual UInt8Array readP25(bool& ret, uint32_t& frameLength); /** * @brief Helper to test if the given buffer contains a complete set of P25 LDU voice vectors for the given DUID type. * @param[in] data Buffer containing the data to check. * @param len Length of the data buffer. * @param[in] duid P25 DUID type. * @returns bool True, if the buffer contains P25 LDU voice vectors for the given DUID type, otherwise false. */ static bool hasLDUVectors(const uint8_t* data, uint32_t len, P25DEF::DUID::E duid); /** * @brief Helper to reconstruct P25 LDU voice vectors from the given DFSI data. * @param[in] dfsiData Buffer containing the DFSI data to reconstruct from. * @param len Length of the DFSI data buffer. * @param[in] dfsiLC Instance of p25::dfsi::LC containing the DFSI link control data. * @param[in] duid P25 DUID type. * @param[out] outLDU Buffer to write the reconstructed LDU voice vectors to. * @returns uint8_t Number of missing frames, 9 if reconstruction failed, otherwise the number of missing frames. */ static uint8_t reconstructLDUVectors(const uint8_t* dfsiData, uint32_t len, p25::dfsi::LC* dfsiLC, P25DEF::DUID::E duid, uint8_t* outLDU); /** * @brief Writes P25 LDU1 frame data to the network. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. * @param[in] data Buffer containing P25 LDU1 data to send. * @param[in] frameType DVM P25 frame type. * @param[in] controlByte DVM Network Control Byte. * @returns bool True, if message was sent, otherwise false. */ virtual bool writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, P25DEF::FrameType::E frameType, uint8_t controlByte = 0U); /** * @brief Writes P25 LDU2 frame data to the network. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. * @param[in] data Buffer containing P25 LDU2 data to send. * @param[in] controlByte DVM Network Control Byte. * @returns bool True, if message was sent, otherwise false. */ virtual bool writeP25LDU2(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, uint8_t controlByte = 0U); /** * @brief Writes P25 TDU frame data to the network. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. * @param[in] controlByte DVM control byte. * @returns bool True, if message was sent, otherwise false. */ virtual bool writeP25TDU(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t controlByte = 0U); /** * @brief Writes P25 TSDU frame data to the network. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] data Buffer containing P25 TSDU data to send. * @returns bool True, if message was sent, otherwise false. */ virtual bool writeP25TSDU(const p25::lc::LC& control, const uint8_t* data); /** * @brief Writes P25 TDULC frame data to the network. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] data Buffer containing P25 TDULC data to send. * @returns bool True, if message was sent, otherwise false. */ virtual bool writeP25TDULC(const p25::lc::LC& control, const uint8_t* data); /** * @brief Writes P25 PDU frame data to the network. * @param[in] dataHeader Instance of p25::data::DataHeader containing PDU header data. * @param currentBlock Current block index being sent. * @param[in] data Buffer containing P25 PDU block data to send. * @param len Length of P25 PDU block data. * @param lastBlock Flag indicating whether or not this is the last block of PDU data. * @returns bool True, if message was sent, otherwise false. */ virtual bool writeP25PDU(const p25::data::DataHeader& header, const uint8_t currentBlock, const uint8_t* data, const uint32_t len, bool lastBlock); /** * @brief Reads P25 Phase 2 raw frame data from the P25 Phase 2 ring buffer. * @param[out] ret Flag indicating whether or not data was received. * @param[out] frameLength Length in bytes of received frame. * @returns UInt8Array Buffer containing received frame. */ virtual UInt8Array readP25P2(bool& ret, uint32_t& frameLength); /** * @brief Writes P25 Phase 2 frame data to the network. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] duid P25 Phase 2 DUID type. * @param[in] slot P25 Phase 2 slot number. * @param[in] data Buffer containing P25 Phase 2 data to send. * @param[in] controlByte DVM control byte. * @returns bool True, if message was sent, otherwise false. */ virtual bool writeP25P2(const p25::lc::LC& control, p25::defines::P2_DUID::E duid, uint8_t slot, const uint8_t* data, const uint8_t controlByte = 0U); /** * @brief Helper to test if the P25 ring buffer has data. * @returns bool True, if the network P25 ring buffer has data, otherwise false. */ bool hasP25Data() const; /** * @brief Helper to test if the P25 Phase 2 ring buffer has data. * @returns bool True, if the network P25 Phase 2 ring buffer has data, otherwise false. */ bool hasP25P2Data() const; /** * @brief Helper to validate a P25 network frame length. * @param frameLength P25 encapsulated frame length. * @param len Network packet length. * @return bool True, if validated, otherwise false. */ bool validateP25FrameLength(uint8_t& frameLength, const uint32_t len, const P25DEF::DUID::E duid); // Next Generation Digital Narrowband /** * @brief Reads NXDN raw frame data from the NXDN ring buffer. * @param[out] ret Flag indicating whether or not data was received. * @param[out] frameLength Length in bytes of received frame. * @returns UInt8Array Buffer containing received frame. */ virtual UInt8Array readNXDN(bool& ret, uint32_t& frameLength); /** * @brief Writes NXDN frame data to the network. * @param[in] lc Instance of nxdn::lc::RTCH containing link control data. * @param[in] data Buffer containing RTCH data to send. * @param[in] len Length of buffer. * @param noSequence Flag indicating the message should be sent with no RTP sequence (65535). * @returns bool True, if message was sent, otherwise false. */ virtual bool writeNXDN(const nxdn::lc::RTCH& lc, const uint8_t* data, const uint32_t len, bool noSequence = false); /** * @brief Helper to test if the NXDN ring buffer has data. * @returns bool True, if the network NXDN ring buffer has data, otherwise false. */ bool hasNXDNData() const; // Analog Audio /** * @brief Reads analog MuLaw audio frame data from the analog ring buffer. * @param[out] ret Flag indicating whether or not data was received. * @param[out] frameLength Length in bytes of received frame. * @returns UInt8Array Buffer containing received frame. */ virtual UInt8Array readAnalog(bool& ret, uint32_t& frameLength); /** * @brief Writes analog MuLaw audio frame data to the network. * @param[in] data Instance of the analog::data::NetData class containing the analog message. * @param noSequence Flag indicating the message should be sent with no RTP sequence (65535). * @returns bool True, if message was sent, otherwise false. */ virtual bool writeAnalog(const analog::data::NetData& data, bool noSequence = false); /** * @brief Helper to test if the analog ring buffer has data. * @returns bool True, if the network analog ring buffer has data, otherwise false. */ bool hasAnalogData() const; public: /** * @brief Gets the peer ID of the network. */ DECLARE_PROTECTED_RO_PROPERTY(uint32_t, peerId, PeerId); /** * @brief Gets the current status of the network. */ DECLARE_PROTECTED_RO_PROPERTY(NET_CONN_STATUS, status, Status); /** * @brief Unix socket storage containing the connected address. */ DECLARE_PROTECTED_RO_PROPERTY_PLAIN(sockaddr_storage, addr); /** * @brief Length of the sockaddr_storage structure. */ DECLARE_PROTECTED_RO_PROPERTY_PLAIN(uint32_t, addrLen); /** * @brief Flag indicating whether network DMR/P25 Phase 2 slot 1 traffic is permitted. */ DECLARE_PROTECTED_RO_PROPERTY(bool, slot1, Slot1); /** * @brief Flag indicating whether network DMR/P25 Phase 2 slot 2 traffic is permitted. */ DECLARE_PROTECTED_RO_PROPERTY(bool, slot2, Slot2); /** * @brief Flag indicating whether network traffic is duplex. */ DECLARE_PROTECTED_RO_PROPERTY(bool, duplex, Duplex); protected: bool m_allowActivityTransfer; bool m_allowDiagnosticTransfer; bool m_packetDump; bool m_debug; udp::Socket* m_socket; FrameQueue* m_frameQueue; RingBuffer m_rxDMRData; RingBuffer m_rxP25Data; RingBuffer m_rxP25P2Data; RingBuffer m_rxNXDNData; RingBuffer m_rxAnalogData; std::mt19937 m_random; uint32_t* m_dmrStreamId; uint32_t m_p25StreamId; uint32_t* m_p25P2StreamId; uint32_t m_nxdnStreamId; uint32_t m_analogStreamId; /** * @brief Helper to update the RTP packet sequence. * @param reset Flag indicating the current RTP packet sequence value should be reset. * @returns uint16_t RTP packet sequence. */ uint16_t pktSeq(bool reset = false); /** * @brief Generates a new stream ID. * @returns uint32_t New stream ID. */ uint32_t createStreamId() { std::uniform_int_distribution dist(DVM_RAND_MIN, DVM_RAND_MAX); return dist(m_random); } /** * @brief Creates an DMR frame message. * \code{.unparsed} * Below is the representation of the data layout for the DMR frame * message header. The header is 20 bytes in length. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Protocol Tag (DMRD) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Seq No. | Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Destination ID | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | Control Flags |S|G| Data Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * S = Slot Number (clear Slot 1, set Slot 2) * G = Group Flag * * The data starting at offset 20 for 33 bytes of the raw DMR frame. * * DMR frame message has 2 trailing bytes: * * Byte 53 54 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | BER | RSSI | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param[out] length Length of network message buffer. * @param streamId Stream ID. * @param data Instance of the dmr::data::Data class containing the DMR message. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::NetData& data); /** * @brief Creates an P25 frame message header. * \code{.unparsed} * Below is the representation of the data layout for the P25 frame * message header. The header is 24 bytes in length. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Protocol Tag (P25D) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | LCO | Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Destination ID | System ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | System ID | Reserved | Control Flags | MFId | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Network ID |S|Rsvd |P2 DUID| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | LSD1 | LSD2 | DUID | Frame Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * S = Slot Number (clear Slot 1, set Slot 2) * * The data starting at offset 20 for variable number of bytes (DUID dependant) * is the P25 frame. * * If the P25 frame message is a LDU1, it contains 13 trailing bytes that * contain the frame type, and encryption data. * * Byte 180 181 182 183 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Frame Type | Algorithm ID | Key ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Message Indicator | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+ * \endcode * @param buffer Buffer to store the P25 network message header. * @param duid P25 DUID. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. * @param[in] frameType DVM P25 frame type. * @param[in] controlByte DVM Network Control Byte. */ void createP25_MessageHdr(uint8_t* buffer, p25::defines::DUID::E duid, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, p25::defines::FrameType::E frameType = p25::defines::FrameType::DATA_UNIT, uint8_t controlByte = 0U); /** * @brief Creates an P25 LDU1 frame message. * * The data packed into a P25 LDU1 frame message is near standard DFSI messaging, just instead of * 9 individual frames, they are packed into a single message one right after another. * * @param[out] length Length of network message buffer. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. * @param[in] data Buffer containing P25 LDU1 data to send. * @param[in] frameType DVM P25 frame type. * @param[in] controlByte DVM Network Control Byte. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createP25_LDU1Message(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, p25::defines::FrameType::E frameType, uint8_t controlByte = 0U); /** * @brief Creates an P25 LDU2 frame message. * * The data packed into a P25 LDU2 frame message is near standard DFSI messaging, just instead of * 9 individual frames, they are packed into a single message one right after another. * * @param[out] length Length of network message buffer. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. * @param[in] data Buffer containing P25 LDU2 data to send. * @param[in] controlByte DVM Network Control Byte. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createP25_LDU2Message(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, uint8_t controlByte = 0U); /** * @brief Creates an P25 TDU frame message. * * The data packed into a P25 TDU frame message is essentially just a message header with control bytes * set. * * @param[out] length Length of network message buffer. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. * @param controlByte DVM control byte. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createP25_TDUMessage(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t controlByte); /** * @brief Creates an P25 TSDU frame message. * * The data packed into a P25 TSDU frame message is essentially just a message header with the FEC encoded * raw TSDU data. * * @param[out] length Length of network message buffer. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] data Buffer containing P25 TSDU data to send. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createP25_TSDUMessage(uint32_t& length, const p25::lc::LC& control, const uint8_t* data); /** * @brief Creates an P25 TDULC frame message. * * The data packed into a P25 TDULC frame message is essentially just a message header with the FEC encoded * raw TDULC data. * * @param[out] length Length of network message buffer. * @param[in] control Instance of p25::lc::LC containing link control data. * @param[in] data Buffer containing P25 TDULC data to send. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createP25_TDULCMessage(uint32_t& length, const p25::lc::LC& control, const uint8_t* data); /** * @brief Creates an P25 PDU frame message. * \code{.unparsed} * Below is the representation of the data layout for the P25 frame * message header used for a PDU. The header is 24 bytes in length. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Protocol Tag (P25D) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |C| SAP | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | PDU Length (Bytes) | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | MFId | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Blk to Flw | Current Block | DUID | Frame Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * C = Confirmed PDU Flag * * The data starting at offset 24 for variable number of bytes (DUID dependant) * is the P25 frame. * \endcode * @param[out] length Length of network message buffer. * @param[in] header Instance of p25::data::DataHeader containing PDU header data. * @param currentBlock Current block index being sent. * @param[in] data Buffer containing P25 PDU block data to send. * @param len Length of P25 PDU block data. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createP25_PDUMessage(uint32_t& length, const p25::data::DataHeader& header, const uint8_t currentBlock, const uint8_t* data, const uint32_t len); /** * @brief Creates an P25 Phase 2 frame message. * \code{.unparsed} * * The data packed into a P25 Phase 2 frame message is essentially just a message header with the FEC encoded * raw Phase 2 data. * * The data starting at offset 24 for 40 bytes of the raw P25 Phase 2 frame. * * \endcode * @param[out] length Length of network message buffer. * @param[in] control Instance of p25::lc::LC containing link control data. * @param duid P25 Phase 2 DUID. * @param[in] slot P25 Phase 2 slot (clear Slot 1, set Slot 2). * @param[in] data Buffer containing P25 LDU2 data to send. * @param[in] controlByte DVM Network Control Byte. * @param data Instance of the dmr::data::Data class containing the DMR message. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createP25P2_Message(uint32_t& length, const p25::lc::LC& control, p25::defines::P2_DUID::E duid, const bool slot, const uint8_t* data, uint8_t controlByte = 0U); /** * @brief Creates an NXDN frame message. * \code{.unparsed} * Below is the representation of the data layout for the NXDN frame * message header. The header is 24 bytes in length. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Protocol Tag (NXDD) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Message Type | Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Destination ID | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | Control Flags |R|G| Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | Frame Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * G = Group Flag * * The data starting at offset 24 for 48 bytes if the raw NXDN frame. * \endcode * @param[out] length Length of network message buffer. * @param[in] lc Instance of nxdn::lc::RTCH containing link control data. * @param[in] data Buffer containing RTCH data to send. * @param[in] len Length of buffer. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createNXDN_Message(uint32_t& length, const nxdn::lc::RTCH& lc, const uint8_t* data, const uint32_t len); /** * @brief Creates an analog frame message. * \code{.unparsed} * Below is the representation of the data layout for the analog frame * message header. The header is 20 bytes in length. * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Protocol Tag (ANOD) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Seq No. | Source ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Destination ID | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | Control Flags | R | Data Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The data starting at offset 20 for 320 bytes of the raw analog frame. * \endcode * @param[out] length Length of network message buffer. * @param streamId Stream ID. * @param data Instance of the analog::data::Data class containing the analog message. * @returns UInt8Array Buffer containing the built network message. */ UInt8Array createAnalog_Message(uint32_t& length, const uint32_t streamId, const analog::data::NetData& data); private: uint16_t m_pktSeq; p25::Audio m_audio; }; } // namespace network #endif // __BASE_NETWORK_H__