From 4a764f67dfda668fecadc4a5be5377da60f15a1e Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 29 Nov 2023 12:18:25 -0500 Subject: [PATCH] [EXPERIMENTAL] add support to configure voice channel iden table IDs; --- configs/config.example.yml | 4 +++- src/host/Host.cpp | 26 +++++++++++++++++------ src/lookups/AffiliationLookup.h | 10 ++++++++- src/monitor/MonitorMainWnd.h | 2 +- src/p25/lc/TSBK.h | 2 +- src/p25/lc/tsbk/IOSP_GRP_VCH.cpp | 7 +++++- src/p25/lc/tsbk/IOSP_UU_VCH.cpp | 7 +++++- src/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp | 7 +++++- src/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp | 14 ++++++++++-- src/p25/packet/ControlSignaling.cpp | 15 +++++++++++-- 10 files changed, 77 insertions(+), 17 deletions(-) diff --git a/configs/config.example.yml b/configs/config.example.yml index 300d4d35..e7ca1b2a 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -365,8 +365,10 @@ system: # Voice Channels # voiceChNo: + # Channel Identity (corresponds to the appropriate entry in the iden_table file). + - channelId: 2 # Channel Number (used to calculate actual host frequency based on the identity table). - - channelNo: 1 + channelNo: 1 # REST API IP Address for voice channel. restAddress: 127.0.0.1 # REST API Port number for voice channel. diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 362ec71f..e351876d 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -1837,7 +1837,7 @@ bool Host::readParams() uint16_t restApiPort = (uint16_t)controlCh["restPort"].as(REST_API_DEFAULT_PORT); std::string restApiPassword = controlCh["restPassword"].as(); - VoiceChData data = VoiceChData(0U, restApiAddress, restApiPort, restApiPassword); + VoiceChData data = VoiceChData(m_channelId, m_channelNo, restApiAddress, restApiPort, restApiPassword); m_controlChData = data; if (!m_controlChData.address().empty() && m_controlChData.port() > 0) { @@ -1860,6 +1860,18 @@ bool Host::readParams() for (size_t i = 0; i < voiceChList.size(); i++) { yaml::Node& channel = voiceChList[i]; + uint8_t chId = (uint8_t)channel["channelId"].as(255U); + + // special case default handling for if the channelId field is missing from the + // configuration + if (chId == 255U) { + chId = m_channelId; + } + + if (chId > 15U) { // clamp to 15 + chId = 15U; + } + uint32_t chNo = (uint32_t)::strtoul(channel["channelNo"].as("1").c_str(), NULL, 16); if (chNo == 0U) { // clamp to 1 chNo = 1U; @@ -1872,19 +1884,21 @@ bool Host::readParams() uint16_t restApiPort = (uint16_t)channel["restPort"].as(REST_API_DEFAULT_PORT); std::string restApiPassword = channel["restPassword"].as(); - ::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Address %s:%u", m_channelId, chNo, restApiAddress.c_str(), restApiPort); + ::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Address %s:%u", chId, chNo, restApiAddress.c_str(), restApiPort); - VoiceChData data = VoiceChData(chNo, restApiAddress, restApiPort, restApiPassword); + VoiceChData data = VoiceChData(chId, chNo, restApiAddress, restApiPort, restApiPassword); m_voiceChData[chNo] = data; m_voiceChNo.push_back(chNo); } std::string strVoiceChNo = ""; for (auto it = m_voiceChNo.begin(); it != m_voiceChNo.end(); ++it) { - int decVal = ::atoi(std::to_string(*it).c_str()); - char hexStr[23]; + uint32_t chNo = ::atoi(std::to_string(*it).c_str()); + ::lookups::VoiceChData voiceChData = m_voiceChData[chNo]; + + char hexStr[29]; - ::sprintf(hexStr, "$%04X (%u)", decVal, decVal); + ::sprintf(hexStr, "$%01X.%01X (%u.%u)", voiceChData.chId(), chNo, voiceChData.chId(), chNo); strVoiceChNo.append(std::string(hexStr)); strVoiceChNo.append(","); diff --git a/src/lookups/AffiliationLookup.h b/src/lookups/AffiliationLookup.h index 9f4aa99e..61f7b858 100644 --- a/src/lookups/AffiliationLookup.h +++ b/src/lookups/AffiliationLookup.h @@ -46,6 +46,7 @@ namespace lookups public: /// Initializes a new instance of the VoiceChData class. VoiceChData() : + m_chId(0U), m_chNo(0U), m_address(), m_port(), @@ -54,11 +55,13 @@ namespace lookups /* stub */ } /// Initializes a new instance of the VoiceChData class. + /// Voice Channel Identity. /// Voice Channel Number. /// REST API Address. /// REST API Port. /// REST API Password. - VoiceChData(uint32_t chNo, std::string address, uint16_t port, std::string password) : + VoiceChData(uint8_t chId, uint32_t chNo, std::string address, uint16_t port, std::string password) : + m_chId(chId), m_chNo(chNo), m_address(address), m_port(port), @@ -73,6 +76,7 @@ namespace lookups VoiceChData & operator=(const VoiceChData & data) { if (this != &data) { + m_chId = data.m_chId; m_chNo = data.m_chNo; m_address = data.m_address; m_port = data.m_port; @@ -82,10 +86,14 @@ namespace lookups return *this; } + /// Helper to determine if the channel identity is valid. + bool isValidChId() { return m_chId != 0U; } /// Helper to determine if the channel is valid. bool isValidCh() { return m_chNo != 0U; } public: + /// Voice Channel Identity. + __READONLY_PROPERTY_PLAIN(uint8_t, chId, chId); /// Voice Channel Number. __READONLY_PROPERTY_PLAIN(uint32_t, chNo, chNo); /// REST API Address. diff --git a/src/monitor/MonitorMainWnd.h b/src/monitor/MonitorMainWnd.h index 43936a9d..e9c10c9c 100644 --- a/src/monitor/MonitorMainWnd.h +++ b/src/monitor/MonitorMainWnd.h @@ -183,7 +183,7 @@ private: ::LogInfoEx(LOG_HOST, "Channel REST API Adddress %s:%u", restApiAddress.c_str(), restApiPort); - VoiceChData data = VoiceChData(0U, restApiAddress, restApiPort, restApiPassword); + VoiceChData data = VoiceChData(0U, 0U, restApiAddress, restApiPort, restApiPassword); NodeStatusWnd* wnd = new NodeStatusWnd(this); wnd->setChData(data); diff --git a/src/p25/lc/TSBK.h b/src/p25/lc/TSBK.h index 61adfa73..7600dbe1 100644 --- a/src/p25/lc/TSBK.h +++ b/src/p25/lc/TSBK.h @@ -126,7 +126,7 @@ namespace p25 __PROTECTED_READONLY_PROPERTY(uint32_t, sysId, SysId); /// Voice channel ID. - __PROTECTED_PROPERTY(uint32_t, grpVchId, GrpVchId); + __PROTECTED_PROPERTY(uint8_t, grpVchId, GrpVchId); /// Voice channel number. __PROTECTED_PROPERTY(uint32_t, grpVchNo, GrpVchNo); diff --git a/src/p25/lc/tsbk/IOSP_GRP_VCH.cpp b/src/p25/lc/tsbk/IOSP_GRP_VCH.cpp index ce9b1d2f..adfa7322 100644 --- a/src/p25/lc/tsbk/IOSP_GRP_VCH.cpp +++ b/src/p25/lc/tsbk/IOSP_GRP_VCH.cpp @@ -93,7 +93,12 @@ void IOSP_GRP_VCH::encode(uint8_t* data, bool rawTSBK, bool noTrellis) (m_emergency ? 0x80U : 0x00U) + // Emergency Flag (m_encrypted ? 0x40U : 0x00U) + // Encrypted Flag (m_priority & 0x07U); // Priority - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID + if (m_grpVchId != 0U) { + tsbkValue = (tsbkValue << 4) + m_grpVchId; // Channel ID + } + else { + tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID + } tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address diff --git a/src/p25/lc/tsbk/IOSP_UU_VCH.cpp b/src/p25/lc/tsbk/IOSP_UU_VCH.cpp index cea30672..f022429c 100644 --- a/src/p25/lc/tsbk/IOSP_UU_VCH.cpp +++ b/src/p25/lc/tsbk/IOSP_UU_VCH.cpp @@ -93,7 +93,12 @@ void IOSP_UU_VCH::encode(uint8_t* data, bool rawTSBK, bool noTrellis) (m_emergency ? 0x80U : 0x00U) + // Emergency Flag (m_encrypted ? 0x40U : 0x00U) + // Encrypted Flag (m_priority & 0x07U); // Priority - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID + if (m_grpVchId != 0U) { + tsbkValue = (tsbkValue << 4) + m_grpVchId; // Channel ID + } + else { + tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID + } tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 24) + m_dstId; // Target ID tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address diff --git a/src/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp b/src/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp index 0245acf5..245c3f3f 100644 --- a/src/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp +++ b/src/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp @@ -74,7 +74,12 @@ void OSP_GRP_VCH_GRANT_UPD::encode(uint8_t* data, bool rawTSBK, bool noTrellis) ulong64_t tsbkValue = 0U; - tsbkValue = m_siteData.channelId(); // Channel ID + if (m_grpVchId != 0U) { + tsbkValue = m_grpVchId; // Channel ID + } + else { + tsbkValue = m_siteData.channelId(); // Channel ID + } tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address tsbkValue = (tsbkValue << 32) + 0; diff --git a/src/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp b/src/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp index 28e70d97..8d32ba09 100644 --- a/src/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp +++ b/src/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp @@ -87,9 +87,19 @@ void OSP_SNDCP_CH_GNT::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = 0U; tsbkValue = (tsbkValue << 8) + m_dataServiceOptions; // Data Service Options - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (T) ID + if (m_grpVchId != 0U) { + tsbkValue = (tsbkValue << 4) + m_grpVchId; // Channel (T) ID + } + else { + tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (T) ID + } tsbkValue = (tsbkValue << 12) + m_dataChannelNo; // Channel (T) Number - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (R) ID + if (m_grpVchId != 0U) { + tsbkValue = (tsbkValue << 4) + m_grpVchId; // Channel (R) ID + } + else { + tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (R) ID + } tsbkValue = (tsbkValue << 12) + (rxChNo & 0xFFFU); // Channel (R) Number tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address diff --git a/src/p25/packet/ControlSignaling.cpp b/src/p25/packet/ControlSignaling.cpp index 8939b7ec..f5022cfe 100644 --- a/src/p25/packet/ControlSignaling.cpp +++ b/src/p25/packet/ControlSignaling.cpp @@ -2235,6 +2235,8 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ } if (chNo > 0U) { + ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); + if (grp) { if (!net) { ::ActivityLog("P25", true, "group grant request from %u to TG %u", srcId, dstId); @@ -2242,7 +2244,6 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ // callback REST API to permit the granted TG on the specified voice channel if (m_p25->m_authoritative && m_p25->m_supervisor) { - ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && chNo != m_p25->m_siteData.channelNo()) { json::object req = json::object(); @@ -2272,6 +2273,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ iosp->setMFId(m_lastMFID); iosp->setSrcId(srcId); iosp->setDstId(dstId); + iosp->setGrpVchId(voiceChData.chId()); iosp->setGrpVchNo(chNo); iosp->setEmergency(emergency); iosp->setEncrypted(encryption); @@ -2298,7 +2300,6 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ // callback REST API to permit the granted TG on the specified voice channel if (m_p25->m_authoritative && m_p25->m_supervisor) { - ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && chNo != m_p25->m_siteData.channelNo()) { json::object req = json::object(); @@ -2328,6 +2329,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ iosp->setMFId(m_lastMFID); iosp->setSrcId(srcId); iosp->setDstId(dstId); + iosp->setGrpVchId(voiceChData.chId()); iosp->setGrpVchNo(chNo); iosp->setEmergency(emergency); iosp->setEncrypted(encryption); @@ -2378,6 +2380,8 @@ void ControlSignaling::writeRF_TSDU_Grant_Update() uint32_t dstId = entry.first; uint32_t chNo = entry.second; + ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); + if (chNo == 0U) { noData = true; m_mbfGrpGrntCnt++; @@ -2387,6 +2391,7 @@ void ControlSignaling::writeRF_TSDU_Grant_Update() // transmit group voice grant update osp->setLCO(TSBK_OSP_GRP_VCH_GRANT_UPD); osp->setDstId(dstId); + osp->setGrpVchId(voiceChData.chId()); osp->setGrpVchNo(chNo); m_mbfGrpGrntCnt++; @@ -2454,6 +2459,9 @@ bool ControlSignaling::writeRF_TSDU_SNDCP_Grant(uint32_t srcId, uint32_t dstId, else { if (m_p25->m_affiliations.grantCh(srcId, srcId, GRANT_TIMER_TIMEOUT, net)) { uint32_t chNo = m_p25->m_affiliations.getGrantedCh(srcId); + ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); + + osp->setGrpVchId(voiceChData.chId()); osp->setGrpVchNo(chNo); osp->setDataChnNo(chNo); m_p25->m_siteData.setChCnt(m_p25->m_affiliations.getRFChCnt() + m_p25->m_affiliations.getGrantedRFChCnt()); @@ -2462,6 +2470,9 @@ bool ControlSignaling::writeRF_TSDU_SNDCP_Grant(uint32_t srcId, uint32_t dstId, } else { uint32_t chNo = m_p25->m_affiliations.getGrantedCh(srcId); + ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); + + osp->setGrpVchId(voiceChData.chId()); osp->setGrpVchNo(chNo); osp->setDataChnNo(chNo);