diff --git a/config.example.yml b/config.example.yml index 27831cf9..239849e0 100644 --- a/config.example.yml +++ b/config.example.yml @@ -87,6 +87,8 @@ protocols: duration: 1 voiceOnControl: false disableCompositeFlag: false + verifyAff: false + verifyReg: false dumpRcchData: false callHang: 5 silenceThreshold: 14 diff --git a/host/calibrate/HostCal.cpp b/host/calibrate/HostCal.cpp index 094fe85e..d4b092ba 100644 --- a/host/calibrate/HostCal.cpp +++ b/host/calibrate/HostCal.cpp @@ -2208,7 +2208,7 @@ void HostCal::processNXDNBER(const uint8_t* buffer) if (usc == NXDN_LICH_USC_SACCH_NS) { if (m_berFrames == 0U) { - LogMessage(LOG_CAL, NXDN_MESSAGE_TYPE_VCALL ", 1031 Test Pattern Start"); + LogMessage(LOG_CAL, "NXDN VCALL (Voice Call), 1031 Test Pattern Start"); timerStart(); m_berErrs = 0U; @@ -2217,7 +2217,7 @@ void HostCal::processNXDNBER(const uint8_t* buffer) return; } else { float ber = float(m_berErrs * 100U) / float(m_berBits); - LogMessage(LOG_CAL, NXDN_MESSAGE_TYPE_TX_REL ", 1031 Test Pattern BER, frames: %u, errs: %.3f%% (%u/%u)", m_berFrames, ber, m_berErrs, m_berBits); + LogMessage(LOG_CAL, "NXDN TX_REL (Transmission Release), 1031 Test Pattern BER, frames: %u, errs: %.3f%% (%u/%u)", m_berFrames, ber, m_berErrs, m_berBits); timerStop(); m_berErrs = 0U; @@ -2240,7 +2240,7 @@ void HostCal::processNXDNBER(const uint8_t* buffer) m_berFrames++; float ber = float(errors) / 1.88F; - LogMessage(LOG_CAL, NXDN_MESSAGE_TYPE_VCALL ", 1031 Test Pattern BER, (errs): %.3f%% (%u/188)", ber, errors); + LogMessage(LOG_CAL, "NXDN VCALL (Voice Call), 1031 Test Pattern BER, (errs): %.3f%% (%u/188)", ber, errors); } } } diff --git a/nxdn/Control.cpp b/nxdn/Control.cpp index 6fc398f9..5018ddd3 100644 --- a/nxdn/Control.cpp +++ b/nxdn/Control.cpp @@ -32,7 +32,10 @@ #include "nxdn/NXDNDefines.h" #include "nxdn/Control.h" #include "nxdn/acl/AccessControl.h" +#include "nxdn/channel/UDCH.h" +#include "nxdn/lc/RTCH.h" #include "nxdn/Sync.h" +#include "nxdn/NXDNUtils.h" #include "edac/AMBEFEC.h" #include "HostMain.h" #include "Log.h" @@ -101,6 +104,7 @@ Control::Control(uint32_t ran, uint32_t callHang, uint32_t queueSize, uint32_t t m_idenTable(idenTable), m_ridLookup(ridLookup), m_tidLookup(tidLookup), + m_affiliations("NXDN Affiliations", verbose), m_idenEntry(), m_queue(queueSize, "NXDN Frame"), m_rfState(RS_RF_LISTENING), @@ -200,6 +204,9 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s yaml::Node systemConf = conf["system"]; yaml::Node nxdnProtocol = conf["protocols"]["nxdn"]; + m_trunk->m_verifyAff = nxdnProtocol["verifyAff"].as(false); + m_trunk->m_verifyReg = nxdnProtocol["verifyReg"].as(false); + yaml::Node control = nxdnProtocol["control"]; m_control = control["enable"].as(false); if (m_control) { @@ -241,12 +248,20 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s } } + std::vector availCh = voiceChNo; + for (auto it = availCh.begin(); it != availCh.end(); ++it) { + m_affiliations.addRFCh(*it); + } + if (printOptions) { LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F); if (m_control) { LogInfo(" Voice on Control: %s", m_voiceOnControl ? "yes" : "no"); } + + LogInfo(" Verify Affiliation: %s", m_trunk->m_verifyAff ? "yes" : "no"); + LogInfo(" Verify Registration: %s", m_trunk->m_verifyReg ? "yes" : "no"); } if (m_voice != NULL) { @@ -286,8 +301,12 @@ bool Control::processFrame(uint8_t* data, uint32_t len) float(m_voice->m_rfFrames) / 12.5F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); } - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_TX_REL ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", - m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); + LogMessage(LOG_RF, "NXDN %s, total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_TX_REL), m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); + + if (m_control) { + m_affiliations.releaseGrant(m_rfLC.getDstId(), false); + } writeEndRF(); return false; @@ -364,7 +383,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) ret = m_data->process(option, data, len); } else { - if (m_voiceOnControl) { + if (m_voiceOnControl && m_affiliations.isChBusy(m_siteData.channelNo())) { ret = m_data->process(option, data, len); } } @@ -374,7 +393,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) ret = m_voice->process(fct, option, data, len); } else { - if (m_voiceOnControl) { + if (m_voiceOnControl && m_affiliations.isChBusy(m_siteData.channelNo())) { ret = m_voice->process(fct, option, data, len); } } @@ -483,8 +502,14 @@ void Control::clock(uint32_t ms) m_networkWatchdog.stop(); - if (m_network != NULL) - m_network->resetNXDN(); + if (m_control) { + m_affiliations.releaseGrant(m_netLC.getDstId(), false); + } + + if (m_dedicatedControl) { + if (m_network != NULL) + m_network->resetNXDN(); + } m_netState = RS_NET_IDLE; @@ -670,6 +695,48 @@ bool Control::writeRF_ControlData() return false; } +/// +/// Helper to write a Tx release packet. +/// +/// +void Control::writeRF_Message_Tx_Rel(bool noNetwork) +{ + uint8_t data[NXDN_FRAME_LENGTH_BYTES + 2U]; + ::memset(data + 2U, 0x00U, NXDN_FRAME_LENGTH_BYTES); + + Sync::addNXDNSync(data + 2U); + + channel::LICH lich; + lich.setRFCT(NXDN_LICH_RFCT_RDCH); + lich.setFCT(NXDN_LICH_USC_UDCH); + lich.setOption(NXDN_LICH_USC_UDCH); + lich.setDirection(NXDN_LICH_DIRECTION_OUTBOUND); + lich.encode(data + 2U); + + uint8_t buffer[NXDN_UDCH_LENGTH_BYTES]; + ::memset(buffer, 0x00U, NXDN_UDCH_LENGTH_BYTES); + + m_rfLC.setMessageType(RTCH_MESSAGE_TYPE_TX_REL); + m_rfLC.encode(buffer, NXDH_UDCH_CRC_BITS); + + channel::UDCH udch; + udch.setRAN(m_ran); + udch.setData(buffer); + udch.encode(data + 2U); + + data[0U] = modem::TAG_DATA; + data[1U] = 0x00U; + + scrambler(data + 2U); + + if (!noNetwork) + m_data->writeNetwork(data, NXDN_FRAME_LENGTH_BYTES + 2U); + + if (m_duplex) { + addFrame(data, NXDN_FRAME_LENGTH_BYTES + 2U); + } +} + /// /// Helper to write RF end of frame data. /// @@ -681,6 +748,10 @@ void Control::writeEndRF() m_rfLC.reset(); m_rfTimeout.stop(); + //m_queue.clear(); + + if (m_network != NULL) + m_network->resetNXDN(); } /// diff --git a/nxdn/Control.h b/nxdn/Control.h index 62f916f0..d3c38b04 100644 --- a/nxdn/Control.h +++ b/nxdn/Control.h @@ -45,6 +45,7 @@ #include "lookups/IdenTableLookup.h" #include "lookups/RadioIdLookup.h" #include "lookups/TalkgroupIdLookup.h" +#include "lookups/AffiliationLookup.h" #include "modem/Modem.h" #include "RingBuffer.h" #include "Timer.h" @@ -137,6 +138,7 @@ namespace nxdn lookups::IdenTableLookup* m_idenTable; lookups::RadioIdLookup* m_ridLookup; lookups::TalkgroupIdLookup* m_tidLookup; + lookups::AffiliationLookup m_affiliations; lookups::IdenTable m_idenEntry; @@ -182,6 +184,9 @@ namespace nxdn /// Helper to write control channel frame data. bool writeRF_ControlData(); + /// Helper to write a Tx release packet. + void writeRF_Message_Tx_Rel(bool noNetwork); + /// void scrambler(uint8_t* data) const; diff --git a/nxdn/NXDNDefines.h b/nxdn/NXDNDefines.h index 11069682..29c11a4a 100644 --- a/nxdn/NXDNDefines.h +++ b/nxdn/NXDNDefines.h @@ -33,11 +33,6 @@ #include "Defines.h" -// Message Type String(s) -#define NXDN_MESSAGE_TYPE_VCALL "MESSAGE_TYPE_VCALL (NXDN Voice Call)" -#define NXDN_MESSAGE_TYPE_TX_REL "MESSAGE_TYPE_TX_REL (NXDN Transmit Release)" -#define NXDN_MESSAGE_TYPE_DCALL "MESSAGE_TYPE_VCALL (NXDN Data Call)" - namespace nxdn { // --------------------------------------------------------------------------- @@ -172,25 +167,42 @@ namespace nxdn const uint8_t DATA_RSP_CLASS_ACK_S = 0x01U; const uint8_t DATA_RSP_CLASS_NACK = 0x03U; - const uint8_t NXDN_CAUSE_RESOURCE_NOT_AVAIL = 0x05U; + const uint8_t NXDN_CAUSE_RSRC_NOT_AVAIL_NETWORK = 0x51U; + const uint8_t NXDN_CAUSE_RSRC_NOT_AVAIL_TEMP = 0x52U; + const uint8_t NXDN_CAUSE_RSRC_NOT_AVAIL_QUEUED = 0x53U; const uint8_t NXDN_CAUSE_SVC_UNAVAILABLE = 0x06U; - const uint8_t NXDN_CAUSE_PROC_ERROR = 0x07U; - - const uint8_t NXDN_CAUSE_MM_NORMAL_1 = 0x01U; - const uint8_t NXDN_CAUSE_MM_NORMAL_2 = 0x04U; - - const uint8_t NXDN_CAUSE_VD_NORMAL_1 = 0x01U; - const uint8_t NXDN_CAUSE_VD_NORMAL_2 = 0x02U; - const uint8_t NXDN_CAUSE_VD_QUEUED = 0x03U; - - const uint8_t NXDN_CAUSE_SS_NORMAL = 0x00U; - const uint8_t NXDN_CAUSE_SS_NORMAL_1 = 0x01U; - const uint8_t NXDN_CAUSE_SS_NORMAL_2 = 0x02U; - - const uint8_t NXDN_CAUSE_DREQ_NORMAL = 0x01U; - - const uint8_t NXDN_CAUSE_DISC_NORMAL = 0x01U; - const uint8_t NXDN_CAUSE_DISC_NORMAL_TC = 0x02U; + const uint8_t NXDN_CAUSE_PROC_ERROR = 0x70U; + const uint8_t NXDN_CAUSE_PROC_ERROR_UNDEF = 0x71U; + + const uint8_t NXDN_CAUSE_MM_REG_ACCEPTED = 0x01U; + const uint8_t NXDN_CAUSE_MM_LOC_ACPT_GRP_FAIL = 0x04U; + const uint8_t NXDN_CAUSE_MM_LOC_ACPT_GRP_REFUSE = 0x04U; + const uint8_t NXDN_CAUSE_MM_REG_FAILED = 0x06U; + const uint8_t NXDN_CAUSE_MM_REG_REFUSED = 0x08U; + + const uint8_t NXDN_CAUSE_VD_ACCEPTED = 0x10U; + const uint8_t NXDN_CAUSE_VD_GRP_NOT_PERM = 0x11U; + const uint8_t NXDN_CAUSE_VD_REQ_UNIT_NOT_PERM = 0x12U; + const uint8_t NXDN_CAUSE_VD_TGT_UNIT_NOT_PERM = 0x13U; + const uint8_t NXDN_CAUSE_VD_REQ_UNIT_NOT_REG = 0x1CU; + const uint8_t NXDN_CAUSE_VD_QUE_CHN_RESOURCE_NOT_AVAIL = 0x30U; + const uint8_t NXDN_CAUSE_VD_QUE_TGT_UNIT_BUSY = 0x38U; + const uint8_t NXDN_CAUSE_VD_QUE_GRP_BUSY = 0x39U; + + const uint8_t NXDN_CAUSE_SS_ACK_R = 0x01U; + const uint8_t NXDN_CAUSE_SS_ACK_S = 0x02U; + const uint8_t NXDN_CAUSE_SS_NACK = 0x08U; + const uint8_t NXDN_CAUSE_SS_ACCEPTED = 0x10U; + const uint8_t NXDN_CAUSE_SS_GRP_NOT_PERM = 0x11U; + const uint8_t NXDN_CAUSE_SS_REQ_UNIT_NOT_PERM = 0x12U; + const uint8_t NXDN_CAUSE_SS_TGT_UNIT_NOT_PERM = 0x13U; + const uint8_t NXDN_CAUSE_SS_REQ_UNIT_NOT_REG = 0x1CU; + + const uint8_t NXDN_CAUSE_DREQ_USER = 0x10U; + const uint8_t NXDN_CAUSE_DREQ_OTHER = 0x1FU; + + const uint8_t NXDN_CAUSE_DISC_USER = 0x10U; + const uint8_t NXDN_CAUSE_DISC_OTHER = 0x1FU; const uint8_t NXDN_SIF1_DATA_CALL_SVC = 0x01U; const uint8_t NXDN_SIF1_VOICE_CALL_SVC = 0x02U; diff --git a/nxdn/lc/RCCH.cpp b/nxdn/lc/RCCH.cpp index d71127f4..575d0bf4 100644 --- a/nxdn/lc/RCCH.cpp +++ b/nxdn/lc/RCCH.cpp @@ -157,7 +157,7 @@ void RCCH::reset() m_version = 0U; - m_causeRsp = NXDN_CAUSE_MM_NORMAL_1; + m_causeRsp = NXDN_CAUSE_MM_REG_ACCEPTED; m_grpVchNo = 0U; @@ -232,7 +232,7 @@ RCCH::RCCH(SiteData siteData) : m_locId(0U), m_regOption(0U), m_version(0U), - m_causeRsp(NXDN_CAUSE_MM_NORMAL_1), + m_causeRsp(NXDN_CAUSE_MM_REG_ACCEPTED), m_grpVchNo(0U), m_callType(CALL_TYPE_UNSPECIFIED), m_emergency(false), diff --git a/nxdn/lc/RTCH.cpp b/nxdn/lc/RTCH.cpp index ce0ff89c..248e58bf 100644 --- a/nxdn/lc/RTCH.cpp +++ b/nxdn/lc/RTCH.cpp @@ -66,7 +66,7 @@ RTCH::RTCH() : m_delayCount(0U), m_algId(NXDN_CIPHER_TYPE_NONE), m_kId(0U), - m_causeRsp(NXDN_CAUSE_VD_NORMAL_1), + m_causeRsp(NXDN_CAUSE_VD_ACCEPTED), m_data(NULL) { m_data = new uint8_t[NXDN_RTCH_LC_LENGTH_BYTES]; @@ -99,7 +99,7 @@ RTCH::RTCH(const RTCH& data) : m_delayCount(0U), m_algId(NXDN_CIPHER_TYPE_NONE), m_kId(0U), - m_causeRsp(NXDN_CAUSE_VD_NORMAL_1), + m_causeRsp(NXDN_CAUSE_VD_ACCEPTED), m_data(NULL) { copy(data); @@ -202,7 +202,7 @@ void RTCH::reset() m_algId = NXDN_CIPHER_TYPE_NONE; m_kId = 0U; - m_causeRsp = NXDN_CAUSE_VD_NORMAL_1; + m_causeRsp = NXDN_CAUSE_VD_ACCEPTED; } /// diff --git a/nxdn/packet/Data.cpp b/nxdn/packet/Data.cpp index d21b5353..c7ca92a0 100644 --- a/nxdn/packet/Data.cpp +++ b/nxdn/packet/Data.cpp @@ -34,6 +34,7 @@ #include "nxdn/packet/Data.h" #include "nxdn/acl/AccessControl.h" #include "nxdn/Sync.h" +#include "nxdn/NXDNUtils.h" #include "edac/CRC.h" #include "HostMain.h" #include "Log.h" @@ -108,7 +109,8 @@ using namespace nxdn::packet; #define VALID_SRCID(_SRC_ID, _DST_ID, _GROUP) \ if (!acl::AccessControl::validateSrcId(_SRC_ID)) { \ if (m_lastRejectId == 0U || m_lastRejectId != _SRC_ID) { \ - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_DCALL " denial, RID rejection, srcId = %u", _SRC_ID); \ + LogWarning(LOG_RF, "NXDN %s denial, RID rejection, srcId = %u", \ + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_DCALL_HDR), _SRC_ID); \ ::ActivityLog("NXDN", true, "RF voice rejection from %u to %s%u ", _SRC_ID, _GROUP ? "TG " : "", _DST_ID); \ m_lastRejectId = _SRC_ID; \ } \ @@ -124,7 +126,8 @@ using namespace nxdn::packet; if (!_GROUP) { \ if (!acl::AccessControl::validateSrcId(_DST_ID)) { \ if (m_lastRejectId == 0 || m_lastRejectId != _DST_ID) { \ - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_DCALL " denial, RID rejection, dstId = %u", _DST_ID); \ + LogWarning(LOG_RF, "NXDN %s denial, RID rejection, dstId = %u", \ + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_DCALL_HDR), _DST_ID); \ ::ActivityLog("NXDN", true, "RF voice rejection from %u to %s%u ", _SRC_ID, _GROUP ? "TG " : "", _DST_ID); \ m_lastRejectId = _DST_ID; \ } \ @@ -138,7 +141,8 @@ using namespace nxdn::packet; else { \ if (!acl::AccessControl::validateTGId(_DST_ID)) { \ if (m_lastRejectId == 0 || m_lastRejectId != _DST_ID) { \ - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_DCALL " denial, TGID rejection, dstId = %u", _DST_ID); \ + LogWarning(LOG_RF, "NXDN %s denial, TGID rejection, dstId = %u", \ + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_DCALL_HDR), _DST_ID); \ ::ActivityLog("NXDN", true, "RF voice rejection from %u to %s%u ", _SRC_ID, _GROUP ? "TG " : "", _DST_ID); \ m_lastRejectId = _DST_ID; \ } \ @@ -216,8 +220,8 @@ bool Data::process(uint8_t option, uint8_t* data, uint32_t len) VALID_DSTID(srcId, dstId, group); if (m_verbose) { - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u", - srcId, dstId, lc.getPacketInfo().getDelivery(), lc.getPacketInfo().getBlockCount(), lc.getPacketInfo().getPadCount(), lc.getPacketInfo().getStart(), lc.getPacketInfo().getFragmentCount()); + LogMessage(LOG_RF, "NXDN %s, srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_DCALL_HDR), srcId, dstId, lc.getPacketInfo().getDelivery(), lc.getPacketInfo().getBlockCount(), lc.getPacketInfo().getPadCount(), lc.getPacketInfo().getStart(), lc.getPacketInfo().getFragmentCount()); } ::ActivityLog("NXDN", true, "RF data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); @@ -267,8 +271,8 @@ bool Data::process(uint8_t option, uint8_t* data, uint32_t len) if (data[0U] == modem::TAG_EOT) { ::ActivityLog("NXDN", true, "RF ended RF data transmission"); - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_TX_REL ", total frames: %d", - m_nxdn->m_voice->m_rfFrames); + LogMessage(LOG_RF, "NXDN %s, total frames: %d", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_TX_REL), m_nxdn->m_voice->m_rfFrames); m_nxdn->writeEndRF(); } @@ -324,8 +328,8 @@ bool Data::processNetwork(uint8_t option, lc::RTCH& netLC, uint8_t* data, uint32 VALID_DSTID(srcId, dstId, group); if (m_verbose) { - LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u", - srcId, dstId, lc.getPacketInfo().getDelivery(), lc.getPacketInfo().getBlockCount(), lc.getPacketInfo().getPadCount(), lc.getPacketInfo().getStart(), lc.getPacketInfo().getFragmentCount()); + LogMessage(LOG_NET, "NXDN %s, srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_DCALL_HDR), srcId, dstId, lc.getPacketInfo().getDelivery(), lc.getPacketInfo().getBlockCount(), lc.getPacketInfo().getPadCount(), lc.getPacketInfo().getStart(), lc.getPacketInfo().getFragmentCount()); } ::ActivityLog("NXDN", false, "network data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); @@ -371,8 +375,8 @@ bool Data::processNetwork(uint8_t option, lc::RTCH& netLC, uint8_t* data, uint32 if (data[0U] == modem::TAG_EOT) { ::ActivityLog("NXDN", true, "network ended RF data transmission"); - LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_TX_REL ", total frames: %d", - m_nxdn->m_voice->m_netFrames); + LogMessage(LOG_NET, "NXDN %s, total frames: %d", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_TX_REL), m_nxdn->m_voice->m_netFrames); m_nxdn->writeEndNet(); } diff --git a/nxdn/packet/Trunk.cpp b/nxdn/packet/Trunk.cpp index f50c0f92..791fdbd8 100644 --- a/nxdn/packet/Trunk.cpp +++ b/nxdn/packet/Trunk.cpp @@ -33,6 +33,7 @@ #include "nxdn/packet/Trunk.h" #include "nxdn/acl/AccessControl.h" #include "nxdn/Sync.h" +#include "nxdn/NXDNUtils.h" #include "edac/CRC.h" #include "HostMain.h" #include "Log.h" @@ -46,6 +47,76 @@ using namespace nxdn::packet; #include #include +// --------------------------------------------------------------------------- +// Macros +// --------------------------------------------------------------------------- + +// Make sure control data is supported. +#define IS_SUPPORT_CONTROL_CHECK(_PCKT, _SRCID) \ + if (!m_nxdn->m_control) { \ + LogWarning(LOG_RF, "NXDN, %s denial, unsupported service, srcId = %u", \ + NXDNUtils::messageTypeToString(_PCKT, true), _SRCID); \ + writeRF_Message_Deny(NXDN_CAUSE_SVC_UNAVAILABLE, _PCKT); \ + m_nxdn->m_rfState = RS_RF_REJECTED; \ + return false; \ + } + +// Validate the source RID. +#define VALID_SRCID(_PCKT, _SRCID, _RSN) \ + if (!acl::AccessControl::validateSrcId(_SRCID)) { \ + LogWarning(LOG_RF, "NXDN, %s denial, RID rejection, srcId = %u", \ + NXDNUtils::messageTypeToString(_PCKT, true), _SRCID); \ + writeRF_Message_Deny(_RSN, _PCKT); \ + m_nxdn->m_rfState = RS_RF_REJECTED; \ + return false; \ + } + +// Validate the target RID. +#define VALID_DSTID(_PCKT, _DSTID, _RSN) \ + if (!acl::AccessControl::validateSrcId(_DSTID)) { \ + LogWarning(LOG_RF, "NXDN, %s denial, RID rejection, dstId = %u", \ + NXDNUtils::messageTypeToString(_PCKT, true), _DSTID); \ + writeRF_Message_Deny(_RSN, _PCKT); \ + m_nxdn->m_rfState = RS_RF_REJECTED; \ + return false; \ + } + +// Validate the talkgroup ID. +#define VALID_TGID(_PCKT, _DSTID, _RSN) \ + if (!acl::AccessControl::validateTGId(_DSTID)) { \ + LogWarning(LOG_RF, "NXDN, %s denial, TGID rejection, dstId = %u", \ + NXDNUtils::messageTypeToString(_PCKT, true), _DSTID); \ + writeRF_Message_Deny(_RSN, _PCKT); \ + m_nxdn->m_rfState = RS_RF_REJECTED; \ + return false; \ + } + +// Verify the source RID is registered. +#define VERIFY_SRCID_REG(_PCKT, _SRCID, _RSN) \ + if (!m_nxdn->m_affiliations.isUnitReg(_SRCID) && m_verifyReg) { \ + LogWarning(LOG_RF, "NXDN, %s denial, RID not registered, srcId = %u", \ + NXDNUtils::messageTypeToString(_PCKT, true), _SRCID); \ + writeRF_Message_Deny(_RSN, _PCKT); \ + m_nxdn->m_rfState = RS_RF_REJECTED; \ + return false; \ + } + +// Verify the source RID is affiliated. +#define VERIFY_SRCID_AFF(_PCKT, _SRCID, _DSTID, _RSN) \ + if (!m_nxdn->m_affiliations.isGroupAff(_SRCID, _DSTID) && m_verifyAff) { \ + LogWarning(LOG_RF, "NXDN, %s denial, RID not affiliated to TGID, srcId = %u, dstId = %u", \ + NXDNUtils::messageTypeToString(_PCKT, true), _SRCID, _DSTID); \ + writeRF_Message_Deny(_RSN, _PCKT); \ + m_nxdn->m_rfState = RS_RF_REJECTED; \ + return false; \ + } + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +const uint32_t GRANT_TIMER_TIMEOUT = 15U; + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -91,14 +162,68 @@ bool Trunk::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) return false; } - // The layer3 data will only be correct if valid is true + RPT_RF_STATE prevRfState = m_nxdn->m_rfState; + if (m_nxdn->m_rfState != RS_RF_DATA) { + m_nxdn->m_rfState = RS_RF_DATA; + } + + m_nxdn->m_queue.clear(); + + // the layer3 data will only be correct if valid is true uint8_t buffer[NXDN_CAC_LENGTH_BYTES]; cac.getData(buffer); m_rfLC.decode(buffer, NXDN_CAC_SHORT_IN_CRC_BITS); - // TODO TODO -- process incoming data + uint16_t srcId = m_rfLC.getSrcId(); + uint16_t dstId = m_rfLC.getDstId(); + + switch (m_rfLC.getMessageType()) { + case RTCH_MESSAGE_TYPE_VCALL: + // make sure control data is supported + IS_SUPPORT_CONTROL_CHECK(RTCH_MESSAGE_TYPE_VCALL, srcId); + + // validate the source RID + VALID_SRCID(RTCH_MESSAGE_TYPE_VCALL, srcId, NXDN_CAUSE_VD_REQ_UNIT_NOT_PERM); + + // validate the talkgroup ID + VALID_TGID(RTCH_MESSAGE_TYPE_VCALL, dstId, NXDN_CAUSE_VD_TGT_UNIT_NOT_PERM); + + // verify the source RID is affiliated + VERIFY_SRCID_AFF(RTCH_MESSAGE_TYPE_VCALL, srcId, dstId, NXDN_CAUSE_VD_REQ_UNIT_NOT_REG); + + if (m_verbose) { + LogMessage(LOG_RF, "NXDN, %s, srcId = %u, dstId = %u", NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL, true), srcId, dstId); + } + + writeRF_Message_Grant(true); + break; + case RCCH_MESSAGE_TYPE_REG: + // make sure control data is supported + IS_SUPPORT_CONTROL_CHECK(RCCH_MESSAGE_TYPE_REG, srcId); + + if (m_verbose) { + LogMessage(LOG_RF, "NXDN %s, srcId = %u", NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_REG, true), srcId); + } + + writeRF_Message_U_Reg_Rsp(srcId); + break; + case RCCH_MESSAGE_TYPE_GRP_REG: + // make sure control data is supported + IS_SUPPORT_CONTROL_CHECK(RCCH_MESSAGE_TYPE_GRP_REG, srcId); + if (m_verbose) { + LogMessage(LOG_RF, "NXDN %s, srcId = %u, dstId = %u", NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_GRP_REG, true), srcId, dstId); + } + + writeRF_Message_Grp_Reg_Rsp(srcId, dstId); + break; + default: + LogError(LOG_RF, "NXDN, unhandled message type, messageType = $%02X", m_rfLC.getMessageType()); + break; + } + + m_nxdn->m_rfState = prevRfState; return true; } @@ -131,7 +256,28 @@ bool Trunk::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t /// void Trunk::clock(uint32_t ms) { - return; + if (m_nxdn->m_control) { + if (m_nxdn->m_network != NULL) { + if (m_nxdn->m_network->isHandlingChGrants() && m_nxdn->m_siteData.netActive()) { + bool grp = true; + uint32_t srcId = 0U; + uint32_t dstId = 0U; + uint32_t grpVchNo = 0U; + + // do we have a grant response? + if (m_nxdn->m_network->readGrantRsp(grp, srcId, dstId, grpVchNo)) { + m_rfLC.setSrcId(srcId); + m_rfLC.setDstId(dstId); + m_rfLC.setGrpVchNo(grpVchNo); + + writeRF_Message_Grant(grp, true, true, true); + } + } + } + + // clock all the grant timers + m_nxdn->m_affiliations.clock(ms); + } } // --------------------------------------------------------------------------- @@ -149,6 +295,8 @@ void Trunk::clock(uint32_t ms) Trunk::Trunk(Control* nxdn, network::BaseNetwork* network, bool dumpRCCHData, bool debug, bool verbose) : m_nxdn(nxdn), m_network(network), + m_verifyAff(false), + m_verifyReg(false), m_rfLC(SiteData(), lookups::IdenTable()), m_netLC(SiteData(), lookups::IdenTable()), m_lastRejectId(0U), @@ -214,11 +362,11 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) switch (n) { case 6: - writeRF_CC_Site_Info(); + writeRF_CC_Message_Site_Info(); break; case 0: default: - writeRF_CC_Service_Info(); + writeRF_CC_Message_Service_Info(); break; } @@ -228,13 +376,328 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) } while (i <= seqCnt); } +/// +/// Helper to write a single-block RCCH packet. +/// +/// +/// +void Trunk::writeRF_Message(bool noNetwork, bool clearBeforeWrite) +{ + if (!m_nxdn->m_control) + return; + + uint8_t data[NXDN_FRAME_LENGTH_BYTES + 2U]; + ::memset(data + 2U, 0x00U, NXDN_FRAME_LENGTH_BYTES); + + Sync::addNXDNSync(data + 2U); + + channel::LICH lich; + lich.setRFCT(NXDN_LICH_RFCT_RCCH); + lich.setFCT(NXDN_LICH_CAC_OUTBOUND); + lich.setOption(NXDN_LICH_DATA_NORMAL); + lich.setDirection(NXDN_LICH_DIRECTION_OUTBOUND); + lich.encode(data + 2U); + + uint8_t buffer[NXDN_RCCH_LC_LENGTH_BYTES]; + ::memset(buffer, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES); + + m_rfLC.encode(buffer, NXDN_CAC_OUT_CRC_BITS); + + channel::CAC cac; + cac.setRAN(m_nxdn->m_ran); + cac.setData(buffer); + cac.encode(data + 2U); + + data[0U] = modem::TAG_DATA; + data[1U] = 0x00U; + + m_nxdn->scrambler(data + 2U); + + if (!noNetwork) + writeNetwork(data, NXDN_FRAME_LENGTH_BYTES + 2U); + + if (clearBeforeWrite) { + m_nxdn->m_modem->clearP25Data(); + m_nxdn->m_queue.clear(); + } + + if (m_nxdn->m_duplex) { + m_nxdn->addFrame(data, NXDN_FRAME_LENGTH_BYTES + 2U); + } +} + +/// +/// Helper to write a grant packet. +/// +/// +/// +/// +/// +/// +bool Trunk::writeRF_Message_Grant(bool grp, bool skip, bool net, bool skipNetCheck) +{ + uint8_t messageType = m_rfLC.getMessageType(); + + // do we have a network connection and are we handling grants at the network? + if (m_nxdn->m_network != NULL) { + if (m_nxdn->m_network->isHandlingChGrants() && m_nxdn->m_siteData.netActive() && !skipNetCheck) { + return m_nxdn->m_network->writeGrantReq(grp, m_rfLC.getSrcId(), m_rfLC.getDstId()); + } + } + + // are we skipping checking? + if (!skip) { + if (m_nxdn->m_rfState != RS_RF_LISTENING && m_nxdn->m_rfState != RS_RF_DATA) { + if (!net) { + LogWarning(LOG_RF, "NXDN, %s denied, traffic in progress, dstId = %u", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL, true), m_rfLC.getDstId()); + writeRF_Message_Deny(NXDN_CAUSE_VD_QUE_GRP_BUSY, RTCH_MESSAGE_TYPE_VCALL); + + ::ActivityLog("NXDN", true, "group grant request from %u to TG %u denied", m_rfLC.getSrcId(), m_rfLC.getDstId()); + m_nxdn->m_rfState = RS_RF_REJECTED; + } + + m_rfLC.setMessageType(messageType); + return false; + } + + if (m_nxdn->m_netState != RS_NET_IDLE && m_rfLC.getDstId() == m_nxdn->m_netLastDstId) { + if (!net) { + LogWarning(LOG_RF, "NXDN, %s denied, traffic in progress, dstId = %u", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL, true), m_rfLC.getDstId()); + writeRF_Message_Deny(NXDN_CAUSE_VD_QUE_GRP_BUSY, RTCH_MESSAGE_TYPE_VCALL); + + ::ActivityLog("NXDN", true, "group grant request from %u to TG %u denied", m_rfLC.getSrcId(), m_rfLC.getDstId()); + m_nxdn->m_rfState = RS_RF_REJECTED; + } + + m_rfLC.setMessageType(messageType); + return false; + } + + // don't transmit grants if the destination ID's don't match and the network TG hang timer is running + if (m_nxdn->m_rfLastDstId != 0U) { + if (m_nxdn->m_rfLastDstId != m_rfLC.getDstId() && (m_nxdn->m_rfTGHang.isRunning() && !m_nxdn->m_rfTGHang.hasExpired())) { + if (!net) { + writeRF_Message_Deny(NXDN_CAUSE_VD_QUE_GRP_BUSY, RTCH_MESSAGE_TYPE_VCALL); + m_nxdn->m_rfState = RS_RF_REJECTED; + } + + m_rfLC.setMessageType(messageType); + return false; + } + } + + if (!m_nxdn->m_affiliations.isGranted(m_rfLC.getDstId())) { + if (!m_nxdn->m_affiliations.isRFChAvailable()) { + if (grp) { + if (!net) { + LogWarning(LOG_RF, "NXDN, %s queued, no channels available, dstId = %u", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL, true), m_rfLC.getDstId()); + writeRF_Message_Deny(NXDN_CAUSE_VD_QUE_CHN_RESOURCE_NOT_AVAIL, RTCH_MESSAGE_TYPE_VCALL); + + ::ActivityLog("NXDN", true, "group grant request from %u to TG %u queued", m_rfLC.getSrcId(), m_rfLC.getDstId()); + m_nxdn->m_rfState = RS_RF_REJECTED; + } + + m_rfLC.setMessageType(messageType); + return false; + } + else { + if (!net) { + LogWarning(LOG_RF, "NXDN, %s queued, no channels available, dstId = %u", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL, true), m_rfLC.getDstId()); + writeRF_Message_Deny(NXDN_CAUSE_VD_QUE_CHN_RESOURCE_NOT_AVAIL, RTCH_MESSAGE_TYPE_VCALL); + + ::ActivityLog("P25", true, "unit-to-unit grant request from %u to %u queued", m_rfLC.getSrcId(), m_rfLC.getDstId()); + m_nxdn->m_rfState = RS_RF_REJECTED; + } + + m_rfLC.setMessageType(messageType); + return false; + } + } + else { + if (m_nxdn->m_affiliations.grantCh(m_rfLC.getDstId(), GRANT_TIMER_TIMEOUT)) { + uint32_t chNo = m_nxdn->m_affiliations.getGrantedCh(m_rfLC.getDstId()); + m_rfLC.setGrpVchNo(chNo); + } + } + } + else { + uint32_t chNo = m_nxdn->m_affiliations.getGrantedCh(m_rfLC.getDstId()); + m_rfLC.setGrpVchNo(chNo); + } + } + + if (grp) { + if (!net) { + ::ActivityLog("NXDN", true, "group grant request from %u to TG %u", m_rfLC.getSrcId(), m_rfLC.getDstId()); + } + } + else { + if (!net) { + ::ActivityLog("NXDN", true, "unit-to-unit grant request from %u to %u", m_rfLC.getSrcId(), m_rfLC.getDstId()); + } + } + + if (m_verbose) { + LogMessage((net) ? LOG_NET : LOG_RF, "NXDN, %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL, true, true), m_rfLC.getEmergency(), m_rfLC.getEncrypted(), + m_rfLC.getPriority(), m_rfLC.getGrpVchNo(), m_rfLC.getSrcId(), m_rfLC.getDstId()); + } + + // transmit group grant + m_rfLC.setMessageType(RTCH_MESSAGE_TYPE_VCALL); + writeRF_Message(false, true); + + m_rfLC.setMessageType(messageType); + return true; +} + +/// +/// Helper to write a deny packet. +/// +/// +/// +void Trunk::writeRF_Message_Deny(uint8_t reason, uint8_t service) +{ + uint8_t messageType = m_rfLC.getMessageType(); + + m_rfLC.setMessageType(service); + m_rfLC.setCauseResponse(reason); + + if (m_verbose) { + LogMessage(LOG_RF, "NXDN, %s, reason = $%02X, service = $%02X, srcId = %u, dstId = %u", + NXDNUtils::messageTypeToString(service, true), service, m_rfLC.getSrcId(), m_rfLC.getDstId()); + } + + writeRF_Message(false); + + m_rfLC.setMessageType(messageType); +} + +/// +/// Helper to write a group registration response packet. +/// +/// +/// +bool Trunk::writeRF_Message_Grp_Reg_Rsp(uint32_t srcId, uint32_t dstId) +{ + bool ret = false; + + m_rfLC.setMessageType(RCCH_MESSAGE_TYPE_GRP_REG); + m_rfLC.setCauseResponse(NXDN_CAUSE_MM_REG_ACCEPTED); + + // validate the location ID + if (m_rfLC.getLocId() != m_nxdn->m_siteData.locId()) { + LogWarning(LOG_RF, "NXDN %s denial, LOCID rejection, locId = $%04X", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_GRP_REG, true), m_rfLC.getLocId()); + ::ActivityLog("NXDN", true, "group affiliation request from %u denied", srcId); + m_rfLC.setCauseResponse(NXDN_CAUSE_MM_REG_FAILED); + } + + // validate the source RID + if (!acl::AccessControl::validateSrcId(srcId)) { + LogWarning(LOG_RF, "NXDN %s denial, RID rejection, srcId = %u", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_GRP_REG, true), srcId); + ::ActivityLog("NXDN", true, "group affiliation request from %u to %s %u denied", srcId, "TG ", dstId); + m_rfLC.setCauseResponse(NXDN_CAUSE_MM_REG_FAILED); + } + + // validate the source RID is registered + if (!m_nxdn->m_affiliations.isUnitReg(srcId) && m_verifyReg) { + LogWarning(LOG_RF, "NXDN %s denial, RID not registered, srcId = %u", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_GRP_REG, true), srcId); + ::ActivityLog("NXDN", true, "group affiliation request from %u to %s %u denied", srcId, "TG ", dstId); + m_rfLC.setCauseResponse(NXDN_CAUSE_MM_REG_REFUSED); + } + + // validate the talkgroup ID + if (m_rfLC.getGroup()) { + if (dstId == 0U) { + LogWarning(LOG_RF, "NXDN %s, TGID 0, dstId = %u", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_GRP_REG, true), dstId); + } + else { + if (!acl::AccessControl::validateTGId(dstId)) { + LogWarning(LOG_RF, "NXDN %s denial, TGID rejection, dstId = %u", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_GRP_REG, true), dstId); + ::ActivityLog("NXDN", true, "group affiliation request from %u to %s %u denied", srcId, "TG ", dstId); + m_rfLC.setCauseResponse(NXDN_CAUSE_MM_LOC_ACPT_GRP_REFUSE); + } + } + } + + if (m_rfLC.getCauseResponse() == NXDN_CAUSE_MM_REG_ACCEPTED) { + if (m_verbose) { + LogMessage(LOG_RF, "NXDN %s, srcId = %u, dstId = %u", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_GRP_REG, true, true), srcId, dstId); + } + + ::ActivityLog("NXDN", true, "group affiliation request from %u to %s %u", srcId, "TG ", dstId); + ret = true; + + // update dynamic affiliation table + m_nxdn->m_affiliations.groupAff(srcId, dstId); + } + + writeRF_Message(false); + return ret; +} + +/// +/// Helper to write a unit registration response packet. +/// +/// +void Trunk::writeRF_Message_U_Reg_Rsp(uint32_t srcId) +{ + m_rfLC.setMessageType(RCCH_MESSAGE_TYPE_REG); + m_rfLC.setCauseResponse(NXDN_CAUSE_MM_REG_ACCEPTED); + + // validate the location ID + if (m_rfLC.getLocId() != m_nxdn->m_siteData.locId()) { + LogWarning(LOG_RF, "NXDN %s denial, LOCID rejection, locId = $%04X", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_REG, true), m_rfLC.getLocId()); + ::ActivityLog("NXDN", true, "unit registration request from %u denied", srcId); + m_rfLC.setCauseResponse(NXDN_CAUSE_MM_REG_FAILED); + } + + // validate the source RID + if (!acl::AccessControl::validateSrcId(srcId)) { + LogWarning(LOG_RF, "NXDN %s denial, RID rejection, srcId = %u", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_REG, true), srcId); + ::ActivityLog("NXDN", true, "unit registration request from %u denied", srcId); + m_rfLC.setCauseResponse(NXDN_CAUSE_MM_REG_FAILED); + } + + if (m_rfLC.getCauseResponse() == NXDN_CAUSE_MM_REG_ACCEPTED) { + if (m_verbose) { + LogMessage(LOG_RF, "NXDN %s, srcId = %u, locId = %u", + NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_REG, true, true), srcId, m_rfLC.getLocId()); + } + + ::ActivityLog("NXDN", true, "unit registration request from %u", srcId); + + // update dynamic unit registration table + if (!m_nxdn->m_affiliations.isUnitReg(srcId)) { + m_nxdn->m_affiliations.unitReg(srcId); + } + } + + m_rfLC.setSrcId(srcId); + m_rfLC.setDstId(srcId); + + writeRF_Message(true); +} + /// /// Helper to write a CC SITE_INFO broadcast packet on the RF interface. /// -void Trunk::writeRF_CC_Site_Info() +void Trunk::writeRF_CC_Message_Site_Info() { if (m_debug) { - LogMessage(LOG_RF, "NXDN, RCCH_MESSAGE_TYPE_SITE_INFO (Site Information)"); + LogMessage(LOG_RF, "NXDN, %s", NXDNUtils::messageTypeToString(RCCH_MESSAGE_TYPE_SITE_INFO)); } uint8_t data[NXDN_FRAME_LENGTH_BYTES + 2U]; @@ -273,10 +736,10 @@ void Trunk::writeRF_CC_Site_Info() /// /// Helper to write a CC SRV_INFO broadcast packet on the RF interface. /// -void Trunk::writeRF_CC_Service_Info() +void Trunk::writeRF_CC_Message_Service_Info() { if (m_debug) { - LogMessage(LOG_RF, "NXDN, MESSAGE_TYPE_SRV_INFO (Service Information)"); + LogMessage(LOG_RF, "NXDN, %s", NXDNUtils::messageTypeToString(MESSAGE_TYPE_SRV_INFO)); } uint8_t data[NXDN_FRAME_LENGTH_BYTES + 2U]; diff --git a/nxdn/packet/Trunk.h b/nxdn/packet/Trunk.h index 6387366f..9da7ddd4 100644 --- a/nxdn/packet/Trunk.h +++ b/nxdn/packet/Trunk.h @@ -78,6 +78,9 @@ namespace nxdn network::BaseNetwork* m_network; + bool m_verifyAff; + bool m_verifyReg; + lc::RCCH m_rfLC; lc::RCCH m_netLC; @@ -99,10 +102,22 @@ namespace nxdn /// Helper to write control channel packet data. void writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS); + /// Helper to write a single-block RCCH packet. + void writeRF_Message(bool noNetwork, bool clearBeforeWrite = false); + + /// Helper to write a grant packet. + bool writeRF_Message_Grant(bool grp, bool skip = false, bool net = false, bool skipNetCheck = false); + /// Helper to write a deny packet. + void writeRF_Message_Deny(uint8_t reason, uint8_t service); + /// Helper to write a group registration response packet. + bool writeRF_Message_Grp_Reg_Rsp(uint32_t srcId, uint32_t dstId); + /// Helper to write a unit registration response packet. + void writeRF_Message_U_Reg_Rsp(uint32_t srcId); + /// Helper to write a CC SITE_INFO broadcast packet on the RF interface. - void writeRF_CC_Site_Info(); + void writeRF_CC_Message_Site_Info(); /// Helper to write a CC SRV_INFO broadcast packet on the RF interface. - void writeRF_CC_Service_Info(); + void writeRF_CC_Message_Service_Info(); }; } // namespace packet } // namespace nxdn diff --git a/nxdn/packet/Voice.cpp b/nxdn/packet/Voice.cpp index 25485b18..b7fd1765 100644 --- a/nxdn/packet/Voice.cpp +++ b/nxdn/packet/Voice.cpp @@ -36,6 +36,7 @@ #include "nxdn/acl/AccessControl.h" #include "nxdn/Audio.h" #include "nxdn/Sync.h" +#include "nxdn/NXDNUtils.h" #include "edac/CRC.h" #include "HostMain.h" #include "Log.h" @@ -110,7 +111,8 @@ using namespace nxdn::packet; #define VALID_SRCID(_SRC_ID, _DST_ID, _GROUP) \ if (!acl::AccessControl::validateSrcId(_SRC_ID)) { \ if (m_lastRejectId == 0U || m_lastRejectId != _SRC_ID) { \ - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_VCALL " denial, RID rejection, srcId = %u", _SRC_ID); \ + LogWarning(LOG_RF, "NXDN, %s denial, RID rejection, srcId = %u", \ + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), _SRC_ID); \ ::ActivityLog("NXDN", true, "RF voice rejection from %u to %s%u ", _SRC_ID, _GROUP ? "TG " : "", _DST_ID); \ m_lastRejectId = _SRC_ID; \ } \ @@ -126,7 +128,8 @@ using namespace nxdn::packet; if (!_GROUP) { \ if (!acl::AccessControl::validateSrcId(_DST_ID)) { \ if (m_lastRejectId == 0 || m_lastRejectId != _DST_ID) { \ - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_VCALL " denial, RID rejection, dstId = %u", _DST_ID); \ + LogWarning(LOG_RF, "NXDN %s denial, RID rejection, dstId = %u", \ + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), _DST_ID); \ ::ActivityLog("NXDN", true, "RF voice rejection from %u to %s%u ", _SRC_ID, _GROUP ? "TG " : "", _DST_ID); \ m_lastRejectId = _DST_ID; \ } \ @@ -140,7 +143,8 @@ using namespace nxdn::packet; else { \ if (!acl::AccessControl::validateTGId(_DST_ID)) { \ if (m_lastRejectId == 0 || m_lastRejectId != _DST_ID) { \ - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_VCALL " denial, TGID rejection, dstId = %u", _DST_ID); \ + LogWarning(LOG_RF, "NXDN %s denial, TGID rejection, dstId = %u", \ + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), _DST_ID); \ ::ActivityLog("NXDN", true, "RF voice rejection from %u to %s%u ", _SRC_ID, _GROUP ? "TG " : "", _DST_ID); \ m_lastRejectId = _DST_ID; \ } \ @@ -237,6 +241,7 @@ bool Voice::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) return false; } + m_nxdn->m_rfLastDstId = lc.getDstId(); m_nxdn->m_rfLC = lc; Sync::addNXDNSync(data + 2U); @@ -282,8 +287,8 @@ bool Voice::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits)); } - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_TX_REL ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", - m_rfFrames, m_rfBits, m_rfUndecodableLC, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); + LogMessage(LOG_RF, "NXDN %s, total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_TX_REL), m_rfFrames, m_rfBits, m_rfUndecodableLC, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); m_nxdn->writeEndRF(); } else { @@ -299,8 +304,8 @@ bool Voice::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) m_nxdn->m_rssiCount = 1U; if (m_verbose) { - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X", - srcId, dstId, group, lc.getEmergency(), encrypted, lc.getPriority(), lc.getAlgId(), lc.getKId()); + LogMessage(LOG_RF, "NXDN %s, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), srcId, dstId, group, lc.getEmergency(), encrypted, lc.getPriority(), lc.getAlgId(), lc.getKId()); } ::ActivityLog("NXDN", true, "RF %svoice transmission from %u to %s%u", encrypted ? "encrypted " : "", srcId, group ? "TG " : "", dstId); @@ -392,6 +397,7 @@ bool Voice::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) // validate destination ID VALID_DSTID(srcId, dstId, group); + m_nxdn->m_rfLastDstId = m_nxdn->m_rfLC.getDstId(); m_rfFrames = 0U; m_rfErrs = 0U; m_rfBits = 1U; @@ -404,8 +410,8 @@ bool Voice::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) m_nxdn->m_rssiCount = 1U; if (m_verbose) { - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X", - srcId, dstId, group, m_nxdn->m_rfLC.getEmergency(), encrypted, m_nxdn->m_rfLC.getPriority(), m_nxdn->m_rfLC.getAlgId(), m_nxdn->m_rfLC.getKId()); + LogMessage(LOG_RF, "NXDN %s, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), srcId, dstId, group, m_nxdn->m_rfLC.getEmergency(), encrypted, m_nxdn->m_rfLC.getPriority(), m_nxdn->m_rfLC.getAlgId(), m_nxdn->m_rfLC.getKId()); } ::ActivityLog("NXDN", true, "RF %slate entry from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId); @@ -497,15 +503,16 @@ bool Voice::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) ::memcpy(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U, NXDN_NULL_AMBE, 9U); ::memcpy(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 27U, NXDN_NULL_AMBE, 9U); - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", exceeded lost audio threshold, filling in"); + LogWarning(LOG_RF, "NXDN %s, exceeded lost audio threshold, filling in", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL)); } m_rfErrs += errors; m_rfBits += 188U; if (m_verbose) { - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/141 (%.1f%%)", - errors, float(errors) / 1.88F); + LogMessage(LOG_RF, "NXDN %s, audio, errs = %u/141 (%.1f%%)", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), errors, float(errors) / 1.88F); } } else if (option == NXDN_LICH_STEAL_FACCH1_1) { channel::FACCH1 facch1; @@ -528,15 +535,16 @@ bool Voice::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) ::memcpy(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U, NXDN_NULL_AMBE, 9U); ::memcpy(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 27U, NXDN_NULL_AMBE, 9U); - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", exceeded lost audio threshold, filling in"); + LogWarning(LOG_RF, "NXDN %s, exceeded lost audio threshold, filling in", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL)); } m_rfErrs += errors; m_rfBits += 94U; if (m_verbose) { - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/94 (%.1f%%)", - errors, float(errors) / 0.94F); + LogMessage(LOG_RF, "NXDN %s, audio, errs = %u/94 (%.1f%%)", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), errors, float(errors) / 0.94F); } } else if (option == NXDN_LICH_STEAL_FACCH1_2) { edac::AMBEFEC ambe; @@ -554,15 +562,16 @@ bool Voice::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) ::memcpy(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U, NXDN_NULL_AMBE, 9U); ::memcpy(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 9U, NXDN_NULL_AMBE, 9U); - LogWarning(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", exceeded lost audio threshold, filling in"); + LogWarning(LOG_RF, "NXDN %s, exceeded lost audio threshold, filling in", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL)); } m_rfErrs += errors; m_rfBits += 94U; if (m_verbose) { - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/94 (%.1f%%)", - errors, float(errors) / 0.94F); + LogMessage(LOG_RF, "NXDN %s, audio, errs = %u/94 (%.1f%%)", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), errors, float(errors) / 0.94F); } channel::FACCH1 facch1; @@ -663,6 +672,7 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t return false; } + m_nxdn->m_netLastDstId = lc.getDstId(); m_nxdn->m_netLC = lc; Sync::addNXDNSync(data + 2U); @@ -699,8 +709,8 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t ::ActivityLog("NXDN", false, "network end of transmission, %.1f seconds", float(m_netFrames) / 12.5F); - LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_TX_REL ", total frames: %d", - m_netFrames); + LogMessage(LOG_NET, "NXDN %s, total frames: %d", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_TX_REL), m_netFrames); m_nxdn->writeEndNet(); } else { @@ -709,8 +719,8 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t m_nxdn->m_netState = RS_NET_AUDIO; if (m_verbose) { - LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X", - srcId, dstId, group, lc.getEmergency(), encrypted, lc.getPriority(), lc.getAlgId(), lc.getKId()); + LogMessage(LOG_NET, "NXDN %s, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), srcId, dstId, group, lc.getEmergency(), encrypted, lc.getPriority(), lc.getAlgId(), lc.getKId()); } ::ActivityLog("NXDN", false, "network %svoice transmission from %u to %s%u", encrypted ? "encrypted " : "", srcId, group ? "TG " : "", dstId); @@ -802,6 +812,7 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t // validate destination ID VALID_DSTID(srcId, dstId, group); + m_nxdn->m_netLastDstId = m_nxdn->m_netLC.getDstId(); m_rfFrames = 0U; m_rfErrs = 0U; m_rfBits = 1U; @@ -809,8 +820,8 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t m_nxdn->m_netState = RS_NET_AUDIO; if (m_verbose) { - LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X", - srcId, dstId, group, m_nxdn->m_netLC.getEmergency(), encrypted, m_nxdn->m_netLC.getPriority(), m_nxdn->m_netLC.getAlgId(), m_nxdn->m_netLC.getKId()); + LogMessage(LOG_NET, "NXDN %s, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), srcId, dstId, group, m_nxdn->m_netLC.getEmergency(), encrypted, m_nxdn->m_netLC.getPriority(), m_nxdn->m_netLC.getAlgId(), m_nxdn->m_netLC.getKId()); } ::ActivityLog("NXDN", false, "network %slate entry from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId); @@ -890,8 +901,8 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t m_rfBits += 188U; if (m_verbose) { - LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/141 (%.1f%%)", - errors, float(errors) / 1.88F); + LogMessage(LOG_NET, "NXDN %s, audio, errs = %u/141 (%.1f%%)", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), errors, float(errors) / 1.88F); } } else if (option == NXDN_LICH_STEAL_FACCH1_1) { channel::FACCH1 facch1; @@ -910,8 +921,8 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t m_rfBits += 94U; if (m_verbose) { - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/94 (%.1f%%)", - errors, float(errors) / 0.94F); + LogMessage(LOG_RF, "NXDN %s, audio, errs = %u/94 (%.1f%%)", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), errors, float(errors) / 0.94F); } } else if (option == NXDN_LICH_STEAL_FACCH1_2) { edac::AMBEFEC ambe; @@ -925,8 +936,8 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t m_rfBits += 94U; if (m_verbose) { - LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/94 (%.1f%%)", - errors, float(errors) / 0.94F); + LogMessage(LOG_RF, "NXDN %s, audio, errs = %u/94 (%.1f%%)", + NXDNUtils::messageTypeToString(RTCH_MESSAGE_TYPE_VCALL), errors, float(errors) / 0.94F); } channel::FACCH1 facch1; bool valid = facch1.decode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS);