From ed4c7aca49b1b488c42031129bc266e51f805c90 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 19 Apr 2022 13:00:09 -0400 Subject: [PATCH] fix issue with RemoteCommand not properly connecting the UDP socket; fix SNDCP_CH_ANN not properly computing Rx frequency; implement TIA-102.AABF-2015 LC_CONV_FALLBACK specification; --- network/RemoteControl.cpp | 16 ++++++++++++++ p25/P25Defines.h | 1 + p25/TrunkPacket.cpp | 46 +++++++++++++++++++++++++++++++++++++++ p25/TrunkPacket.h | 6 +++++ p25/lc/TDULC.cpp | 13 +++++++++-- p25/lc/TSBK.cpp | 16 +++++++++++--- remote/RemoteCommand.cpp | 21 ++++++++++-------- 7 files changed, 105 insertions(+), 14 deletions(-) diff --git a/network/RemoteControl.cpp b/network/RemoteControl.cpp index 229621e0..5d9bd2d8 100644 --- a/network/RemoteControl.cpp +++ b/network/RemoteControl.cpp @@ -64,6 +64,7 @@ using namespace modem; #define RCD_DMR_BEACON_CMD "dmr-beacon" #define RCD_P25_CC_CMD "p25-cc" +#define RCD_P25_CC_FALLBACK "p25-cc-fallback" #define RCD_DMRD_MDM_INJ_CMD "dmrd-mdm-inj" #define RCD_P25D_MDM_INJ_CMD "p25d-mdm-inj" @@ -315,6 +316,21 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25) LogError(LOG_RCON, CMD_FAILED_STR "P25 mode is not enabled!"); } } + else if (rcom == RCD_P25_CC_FALLBACK) { + // Command is in the form of: "p25-cc-fallback 0/1" + uint8_t fallback = getArgUInt8(args, 0U); + if (p25 != NULL) { + if (host->m_controlData) { + p25->trunk()->setConvFallback((fallback == 1U) ? true : false); + } + else { + LogError(LOG_RCON, CMD_FAILED_STR "P25 control data is not enabled!"); + } + } + else { + LogError(LOG_RCON, CMD_FAILED_STR "P25 mode is not enabled!"); + } + } else if (rcom == RCD_DMRD_MDM_INJ_CMD && argCnt >= 1U) { // Command is in the form of: "dmrd-mdm-inj if (dmr != NULL) { diff --git a/p25/P25Defines.h b/p25/P25Defines.h index 91a69244..5b0ec532 100644 --- a/p25/P25Defines.h +++ b/p25/P25Defines.h @@ -278,6 +278,7 @@ namespace p25 const uint8_t LC_ADJ_STS_BCAST = 0x22U; // ADJ STS BCAST - Adjacent Site Status Broadcast const uint8_t LC_RFSS_STS_BCAST = 0x23U; // RFSS STS BCAST - RFSS Status Broadcast const uint8_t LC_NET_STS_BCAST = 0x24U; // NET STS BCAST - Network Status Broadcast + const uint8_t LC_CONV_FALLBACK = 0x2AU; // CONV FALLBACK - Conventional Fallback // TSBK ISP/OSP Shared Opcode(s) const uint8_t TSBK_IOSP_GRP_VCH = 0x00U; // GRP VCH REQ - Group Voice Channel Request (ISP), GRP VCH GRANT - Group Voice Channel Grant (OSP) diff --git a/p25/TrunkPacket.cpp b/p25/TrunkPacket.cpp index cedb5379..5f518ea8 100644 --- a/p25/TrunkPacket.cpp +++ b/p25/TrunkPacket.cpp @@ -131,6 +131,7 @@ const uint32_t ADJ_SITE_UPDATE_CNT = 5U; const uint32_t TSDU_CTRL_BURST_COUNT = 2U; const uint32_t TSBK_MBF_CNT = 3U; const uint32_t GRANT_TIMER_TIMEOUT = 15U; +const uint8_t CONV_FALLBACK_PACKET_DELAY = 8U; // --------------------------------------------------------------------------- // Public Class Members @@ -1233,6 +1234,29 @@ void TrunkPacket::writeRF_TSDU_Mot_Patch(uint32_t group1, uint32_t group2, uint3 m_rfTSBK.setMFId(P25_MFG_STANDARD); } +/// +/// Helper to change the conventional fallback state. +/// +/// Flag indicating whether conventional fallback is enabled. +void TrunkPacket::setConvFallback(bool fallback) +{ + m_convFallback = fallback; + if (m_convFallback && m_p25->m_control) { + m_convFallbackPacketDelay = 0U; + uint8_t lco = m_rfTSBK.getLCO(); + + m_rfTSBK.setLCO(TSBK_OSP_MOT_PSH_CCH); + m_rfTSBK.setMFId(P25_MFG_MOT); + + for (uint8_t i = 0U; i < 3U; i++) { + writeRF_TSDU_SBF(true); + } + + m_rfTSBK.setLCO(lco); + m_rfTSBK.setMFId(P25_MFG_STANDARD); + } +} + /// /// Helper to change the TSBK verbose state. /// @@ -1281,6 +1305,8 @@ TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool dumpT m_noStatusAck(false), m_noMessageAck(true), m_unitToUnitAvailCheck(true), + m_convFallbackPacketDelay(0U), + m_convFallback(false), m_adjSiteUpdateTimer(1000U), m_adjSiteUpdateInterval(ADJ_SITE_TIMER_TIMEOUT), m_ctrlTSDUMBF(true), @@ -1351,6 +1377,26 @@ void TrunkPacket::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) resetRF(); + if (m_convFallback) { + bool fallbackTx = (frameCnt % 253U) == 0U; + if (fallbackTx && n == 7U) { + if (m_convFallbackPacketDelay >= CONV_FALLBACK_PACKET_DELAY) { + lc::TDULC lc = lc::TDULC(m_p25->m_siteData, m_p25->m_idenEntry, m_dumpTSBK); + lc.setLCO(LC_CONV_FALLBACK); + + for (uint8_t i = 0U; i < 3U; i++) { + writeRF_TDULC(lc, true); + } + + m_convFallbackPacketDelay = 0U; + } else { + m_convFallbackPacketDelay++; + } + } + + return; + } + if (m_debug) { LogDebug(LOG_P25, "writeRF_ControlData, mbfCnt = %u, frameCnt = %u, seq = %u, adjSS = %u", m_mbfCnt, frameCnt, n, adjSS); } diff --git a/p25/TrunkPacket.h b/p25/TrunkPacket.h index a29092ec..b74c4ea4 100644 --- a/p25/TrunkPacket.h +++ b/p25/TrunkPacket.h @@ -107,6 +107,9 @@ namespace p25 /// Helper to write a Motorola patch packet. void writeRF_TSDU_Mot_Patch(uint32_t group1, uint32_t group2, uint32_t group3); + /// Helper to change the conventional fallback state. + void setConvFallback(bool fallback); + /// Helper to change the TSBK verbose state. void setTSBKVerbose(bool verbose); @@ -154,6 +157,9 @@ namespace p25 bool m_noStatusAck; bool m_noMessageAck; bool m_unitToUnitAvailCheck; + + uint8_t m_convFallbackPacketDelay; + bool m_convFallback; Timer m_adjSiteUpdateTimer; uint32_t m_adjSiteUpdateInterval; diff --git a/p25/lc/TDULC.cpp b/p25/lc/TDULC.cpp index 6c252e7b..10381f3b 100644 --- a/p25/lc/TDULC.cpp +++ b/p25/lc/TDULC.cpp @@ -446,7 +446,7 @@ void TDULC::encodeLC(uint8_t* rs) break; case LC_SYS_SRV_BCAST: rs[0U] |= 0x40U; // Implicit Operation - rsValue = 0U; // + rsValue = 0U; rsValue = (rsValue << 16) + services; // System Services Available rsValue = (rsValue << 24) + services; // System Services Supported break; @@ -485,13 +485,22 @@ void TDULC::encodeLC(uint8_t* rs) break; case LC_NET_STS_BCAST: rs[0U] |= 0x40U; // Implicit Operation - rsValue = 0U; // + rsValue = 0U; rsValue = (rsValue << 20) + m_siteData.netId(); // Network ID rsValue = (rsValue << 12) + m_siteData.sysId(); // System ID rsValue = (rsValue << 4) + m_siteData.channelId(); // Channel ID rsValue = (rsValue << 12) + m_siteData.channelNo(); // Channel Number rsValue = (rsValue << 8) + m_siteData.serviceClass(); // System Service Class break; + case LC_CONV_FALLBACK: + rsValue = 0U; + rsValue = (rsValue << 48) + m_siteData.channelId(); // Channel ID 6 + rsValue = (rsValue << 40) + m_siteData.channelId(); // Channel ID 5 + rsValue = (rsValue << 32) + m_siteData.channelId(); // Channel ID 4 + rsValue = (rsValue << 24) + m_siteData.channelId(); // Channel ID 3 + rsValue = (rsValue << 16) + m_siteData.channelId(); // Channel ID 2 + rsValue = (rsValue << 8) + m_siteData.channelId(); // Channel ID 1 + break; default: LogError(LOG_P25, "TDULC::encodeLC(), unknown LC value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); break; diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp index e7f1f0a3..4de252d2 100644 --- a/p25/lc/TSBK.cpp +++ b/p25/lc/TSBK.cpp @@ -545,7 +545,7 @@ bool TSBK::decode(const uint8_t* data, bool rawTSBK) /// /// /// -void TSBK::encode(uint8_t * data, bool rawTSBK, bool noTrellis) +void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis) { assert(data != NULL); @@ -634,6 +634,15 @@ void TSBK::encode(uint8_t * data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_OSP_SNDCP_CH_ANN: + { + uint32_t calcSpace = (uint32_t)(m_siteIdenEntry.chSpaceKhz() / 0.125); + float calcTxOffset = m_siteIdenEntry.txOffsetMhz() * 1000000; + + uint32_t rxFrequency = (uint32_t)((m_siteIdenEntry.baseFrequency() + ((calcSpace * 125) * m_siteData.channelNo())) + calcTxOffset); + + uint32_t rxRootFrequency = rxFrequency - m_siteIdenEntry.baseFrequency(); + uint32_t rxChannelNo = rxRootFrequency / (m_siteIdenEntry.chSpaceKhz() * 1000); + tsbkValue = 0U; // tsbkValue = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag (m_encrypted ? 0x40U : 0x00U); // Encrypted Flag @@ -643,9 +652,10 @@ void TSBK::encode(uint8_t * data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (T) ID tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel (T) Number tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (R) ID - tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel (R) Number + tsbkValue = (tsbkValue << 12) + rxChannelNo; // Channel (R) Number tsbkValue = (tsbkValue << 16) + m_sndcpDAC; // Data Access Control - break; + } + break; case TSBK_IOSP_U_REG: tsbkValue = 0U; tsbkValue = (tsbkValue << 2) + (m_response & 0x3U); // Unit Registration Response diff --git a/remote/RemoteCommand.cpp b/remote/RemoteCommand.cpp index c27173e3..20772b3d 100644 --- a/remote/RemoteCommand.cpp +++ b/remote/RemoteCommand.cpp @@ -50,6 +50,11 @@ using namespace network; #undef __EXE_NAME__ #define __EXE_NAME__ "dvmcmd" +#define ERRNO_REMOTE_CMD 99 +#define ERRNO_SOCK_OPEN 98 +#define ERRNO_ADDR_LOOKUP 97 +#define ERRNO_FAILED_TO_SEND 96 + const uint32_t START_OF_TEXT = 0x02; const uint32_t REC_SEPARATOR = 0x1E; @@ -181,7 +186,7 @@ int main(int argc, char** argv) if (argc < 2) { usage("error: %s", "must specify the remote command!"); - return EXIT_FAILURE; + return ERRNO_REMOTE_CMD; } if (argc > 1) { @@ -255,7 +260,7 @@ int CRemoteCommand::send(const std::string& command) bool ret = socket.open(); if (!ret) - return EXIT_FAILURE; + return ERRNO_SOCK_OPEN; uint8_t buffer[RC_BUFFER_LENGTH]; ::memset(buffer, 0x00U, RC_BUFFER_LENGTH); @@ -263,11 +268,9 @@ int CRemoteCommand::send(const std::string& command) sockaddr_storage addr; uint32_t addrLen; - if (UDPSocket::lookup(m_address, m_port, addr, addrLen) != 0) - addrLen = 0U; - - if (addrLen > 0U) { - return EXIT_FAILURE; + if (UDPSocket::lookup(m_address, m_port, addr, addrLen) != 0) { + ::LogError(LOG_HOST, "Could not lookup the address of remote"); + return ERRNO_ADDR_LOOKUP; } ::LogInfoEx(LOG_HOST, "%s: sending command \"%s\" to %s:%u\r\n", g_progExe.c_str(), command.c_str(), @@ -294,11 +297,11 @@ int CRemoteCommand::send(const std::string& command) buffer[33U] = REC_SEPARATOR; ::memcpy(buffer + 34U, command.c_str(), command.size()); - ret = socket.write((uint8_t *)buffer, 34U + command.size(), addr, m_port); + ret = socket.write((uint8_t *)buffer, 34U + command.size(), addr, addrLen); if (!ret) { socket.close(); ::LogError(LOG_HOST, "Failed to send command: \"%s\"\r\n", command.c_str()); - return EXIT_FAILURE; + return ERRNO_FAILED_TO_SEND; } socket.close();