diff --git a/CMakeLists.txt b/CMakeLists.txt index b4d5190f..6f4b2c2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,15 +57,15 @@ if (COMPILE_WIN32) set(ENABLE_TCP_SSL OFF) message(CHECK_START "Enable TCP SSL support - no; for simplicity WIN32 does not support TCP SSL.") else() - set(CMAKE_C_COMPILER /usr/bin/gcc) - set(CMAKE_CXX_COMPILER /usr/bin/g++) + set(CMAKE_C_COMPILER /usr/bin/gcc CACHE STRING "C compiler") + set(CMAKE_CXX_COMPILER /usr/bin/g++ CACHE STRING "C++ compiler") set(ARCH amd64) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64) endif (COMPILE_WIN32) if (CROSS_COMPILE_ARM) - set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc) - set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++) + set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc CACHE STRING "C compiler") + set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++ CACHE STRING "C++ compiler") set(ARCH armhf) set(CMAKE_SYSTEM_PROCESSOR armhf) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE armhf) @@ -73,8 +73,8 @@ if (CROSS_COMPILE_ARM) message(CHECK_START "Cross compiling for 32-bit ARM - ${CMAKE_C_COMPILER}") endif (CROSS_COMPILE_ARM) if (CROSS_COMPILE_AARCH64) - set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc) - set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++) + set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc CACHE STRING "C compiler") + set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++ CACHE STRING "C++ compiler") set(ARCH arm64) set(CMAKE_SYSTEM_PROCESSOR arm64) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE arm64) @@ -97,16 +97,16 @@ if (CROSS_COMPILE_RPI_ARM) GIT_REPOSITORY https://github.com/raspberrypi/tools.git ) FetchContent_MakeAvailable(RPiTools) - set(CMAKE_C_COMPILER ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc) - set(CMAKE_CXX_COMPILER ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-g++) + set(CMAKE_C_COMPILER ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc CACHE STRING "C compiler" FORCE) + set(CMAKE_CXX_COMPILER ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ CACHE STRING "C++ compiler" FORCE) message(CHECK_START "Apply OpenSSL library binaries for cross compling (old RPi) 32-bit ARM - ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src") execute_process(COMMAND tar xzf contrib/openssl_cross_patch.RPI_ARM.tar.gz -C ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) set(OPENSSL_ROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot/usr/lib) else() - set(CMAKE_C_COMPILER ${RPI_ARM_TOOLS}/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc) - set(CMAKE_CXX_COMPILER ${RPI_ARM_TOOLS}/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-g++) + set(CMAKE_C_COMPILER ${RPI_ARM_TOOLS}/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc CACHE STRING "C compiler" FORCE) + set(CMAKE_CXX_COMPILER ${RPI_ARM_TOOLS}/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ CACHE STRING "C++ compiler" FORCE) message(CHECK_START "Apply OpenSSL library binaries for cross compling (old RPi) 32-bit ARM - ${RPI_ARM_TOOLS}") execute_process(COMMAND tar xzf contrib/openssl_cross_patch.RPI_ARM.tar.gz -C ${RPI_ARM_TOOLS} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) @@ -538,7 +538,7 @@ add_custom_target(old_install COMMAND install -m 644 ../configs/fne-config.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/fne-config.example.yml COMMAND install -m 644 ../configs/fne-sysview.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/fne-sysview.example.yml COMMAND install -m 644 ../configs/monitor-config.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/monitor-config.example.yml - COMMAND install -m 644 ../configs/iden_table.dat ${CMAKE_LEGACY_INSTALL_PREFIX}/iden_table.dat + COMMAND install -m 644 ../configs/iden_table.example.dat ${CMAKE_LEGACY_INSTALL_PREFIX}/iden_table.example.dat COMMAND install -m 644 ../configs/RSSI.dat ${CMAKE_LEGACY_INSTALL_PREFIX}/RSSI.dat COMMAND install -m 644 ../configs/rid_acl.example.dat ${CMAKE_LEGACY_INSTALL_PREFIX}/rid_acl.dat COMMAND install -m 644 ../configs/talkgroup_rules.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/talkgroup_rules.example.yml @@ -580,9 +580,9 @@ add_custom_target(old_install-service COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/fne-config.example.yml COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/fne-sysview.example.yml COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/monitor-config.example.yml - COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/iden_table.dat + COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/iden_table.example.dat COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/RSSI.dat - COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/rid_acl.dat + COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/rid_acl.example.dat COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/talkgroup_rules.example.yml COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/bridge-config.example.yml COMMAND chown dvmhost:dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/log diff --git a/configs/bridge-config.example.yml b/configs/bridge-config.example.yml index 4b3540b9..633543ee 100644 --- a/configs/bridge-config.example.yml +++ b/configs/bridge-config.example.yml @@ -73,6 +73,9 @@ network: # Flag indicating UDP audio should be transmitted without the length leader. # NOTE: This flag is only applicable when encoding G.711 uLaw. udpNoIncludeLength: false + # Flag indicating UDP audio should be RTP framed. + # NOTE: This flag is only applicable when encoding G.711 uLaw. + udpRTPFrames: false # Source "Radio ID" for transmitted audio frames. sourceId: 1234567 @@ -140,3 +143,8 @@ system: # Enable local audio over speakers. localAudio: true + + # Flag indicating whether or not trace logging is enabled. + trace: false + # Flag indicating whether or not debug logging is enabled. + debug: false diff --git a/configs/config.example.yml b/configs/config.example.yml index 7609de15..0ac084d6 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -225,6 +225,8 @@ protocols: legacyGroupReg: false # Flag indicating the host should send a network grant demand TDU for conventional traffic. convNetGrantDemand: false + # Flag indicating the host should demand a full unit registration for a refused affiliation request. + demandUnitRegForRefusedAff: true # Flag indicating the host should verify group affiliation. verifyAff: false # Flag indicating the host should verify unit registration. diff --git a/configs/iden_table.dat b/configs/iden_table.example.dat similarity index 100% rename from configs/iden_table.dat rename to configs/iden_table.example.dat diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index bd625428..17b541ef 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -19,6 +19,7 @@ #include "common/p25/dfsi/LC.h" #include "common/p25/lc/LC.h" #include "common/p25/P25Utils.h" +#include "common/network/RTPHeader.h" #include "common/network/udp/Socket.h" #include "common/Log.h" #include "common/StopWatch.h" @@ -30,6 +31,7 @@ #include "SampleTimeConversion.h" using namespace network; +using namespace network::frame; using namespace network::udp; #include @@ -67,6 +69,8 @@ static short seg_uend[8] = { 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FF #define BIAS (0x84) // bias for linear code #define CLIP 8159 +const uint8_t RTP_G711_PAYLOAD_TYPE = 0x00U; + // --------------------------------------------------------------------------- // Static Class Members // --------------------------------------------------------------------------- @@ -161,17 +165,17 @@ static short search(short val, short* table, short size) /* Helper to convert PCM into G.711 aLaw. */ -uint8_t pcmToaLaw(short pcm) +uint8_t encodeALaw(short pcm) { short mask; - unsigned char aval; + uint8_t aval; pcm = pcm >> 3; if (pcm >= 0) { - mask = 0xD5; // sign (7th) bit = 1 + mask = 0xD5U; // sign (7th) bit = 1 } else { - mask = 0x55; // sign bit = 0 + mask = 0x55U; // sign bit = 0 pcm = -pcm - 1; } @@ -196,9 +200,9 @@ uint8_t pcmToaLaw(short pcm) /* Helper to convert G.711 aLaw into PCM. */ -short aLawToPCM(uint8_t alaw) +short decodeALaw(uint8_t alaw) { - alaw ^= 0x55; + alaw ^= 0x55U; short t = (alaw & QUANT_MASK) << 4; short seg = ((unsigned)alaw & SEG_MASK) >> SEG_SHIFT; @@ -207,19 +211,19 @@ short aLawToPCM(uint8_t alaw) t += 8; break; case 1: - t += 0x108; + t += 0x108U; break; default: - t += 0x108; + t += 0x108U; t <<= seg - 1; } return ((alaw & SIGN_BIT) ? t : -t); } -/* Helper to convert PCM into G.711 uLaw. */ +/* Helper to convert PCM into G.711 MuLaw. */ -uint8_t pcmTouLaw(short pcm) +uint8_t encodeMuLaw(short pcm) { short mask; @@ -252,9 +256,9 @@ uint8_t pcmTouLaw(short pcm) } } -/* Helper to convert G.711 uLaw into PCM. */ +/* Helper to convert G.711 MuLaw into PCM. */ -short uLawToPCM(uint8_t ulaw) +short decodeMuLaw(uint8_t ulaw) { // complement to obtain normal u-law value ulaw = ~ulaw; @@ -288,6 +292,7 @@ HostBridge::HostBridge(const std::string& confFile) : m_udpReceiveAddress("127.0.0.1"), m_udpNoIncludeLength(false), m_udpUseULaw(false), + m_udpRTPFrames(false), m_srcId(p25::defines::WUID_FNE), m_srcIdOverride(0U), m_overrideSrcIdFromMDC(false), @@ -345,7 +350,10 @@ HostBridge::HostBridge(const std::string& confFile) : m_detectedSampleCnt(0U), m_dumpSampleLevel(false), m_running(false), - m_debug(false) + m_trace(false), + m_debug(false), + m_rtpSeqNo(0U), + m_rtpTimestamp(INVALID_TS) #if defined(_WIN32) , m_encoderState(nullptr), @@ -962,6 +970,9 @@ bool HostBridge::readParams() yaml::Node networkConf = m_conf["network"]; m_udpAudio = networkConf["udpAudio"].as(false); + m_trace = systemConf["trace"].as(false); + m_debug = systemConf["debug"].as(false); + LogInfo("General Parameters"); LogInfo(" Rx Audio Gain: %.1f", m_rxAudioGain); LogInfo(" Vocoder Decoder Audio Gain: %.1f", m_vocoderDecoderAudioGain); @@ -980,6 +991,10 @@ bool HostBridge::readParams() LogInfo(" Local Audio: %s", m_localAudio ? "yes" : "no"); LogInfo(" UDP Audio: %s", m_udpAudio ? "yes" : "no"); + if (m_debug) { + LogInfo(" Debug: yes"); + } + return true; } @@ -1005,8 +1020,12 @@ bool HostBridge::createNetwork() m_udpReceiveAddress = networkConf["udpReceiveAddress"].as(); m_udpUseULaw = networkConf["udpUseULaw"].as(false); - if (m_udpUseULaw) + if (m_udpUseULaw) { m_udpNoIncludeLength = networkConf["udpNoIncludeLength"].as(false); + m_udpRTPFrames = networkConf["udpRTPFrames"].as(false); + if (m_udpRTPFrames) + m_udpNoIncludeLength = true; // RTP disables the length being included + } if (m_udpUseULaw && m_udpMetadata) m_udpMetadata = false; // metadata isn't supported when encoding uLaw @@ -1072,9 +1091,10 @@ bool HostBridge::createNetwork() LogInfo(" UDP Audio Send Port: %u", m_udpSendPort); LogInfo(" UDP Audio Receive Address: %s", m_udpReceiveAddress.c_str()); LogInfo(" UDP Audio Receive Port: %u", m_udpReceivePort); - LogInfo(" UDP Audio Use uLaw Encoding: %u", m_udpUseULaw ? "yes" : "no"); + LogInfo(" UDP Audio Use uLaw Encoding: %s", m_udpUseULaw ? "yes" : "no"); if (m_udpUseULaw) { - LogInfo(" UDP Audio No Length Header: %u", m_udpNoIncludeLength ? "yes" : "no"); + LogInfo(" UDP Audio No Length Header: %s", m_udpNoIncludeLength ? "yes" : "no"); + LogInfo(" UDP Audio RTP Framed: %s", m_udpRTPFrames ? "yes" : "no"); } } @@ -1148,8 +1168,8 @@ void HostBridge::processUDPAudio() } if (length > 0) { - if (m_debug) - Utils::dump(1U, "UDP Audio Network Packet", buffer, length); + if (m_trace) + Utils::dump(1U, "HostBridge()::processUDPAudio() Audio Network Packet", buffer, length); uint32_t pcmLength = 0; if (m_udpNoIncludeLength) { @@ -1158,14 +1178,30 @@ void HostBridge::processUDPAudio() pcmLength = __GET_UINT32(buffer, 0U); } + if (m_udpRTPFrames) + pcmLength = MBE_SAMPLES_LENGTH * 2U; + UInt8Array __pcm = std::make_unique(pcmLength); uint8_t* pcm = __pcm.get(); - if (m_udpNoIncludeLength) { - ::memcpy(pcm, buffer, pcmLength); + if (m_udpRTPFrames) { + RTPHeader rtpHeader = RTPHeader(); + rtpHeader.decode(buffer); + + if (rtpHeader.getPayloadType() != RTP_G711_PAYLOAD_TYPE) { + LogError(LOG_HOST, "Invalid RTP payload type %u", rtpHeader.getPayloadType()); + return; + } + + ::memcpy(pcm, buffer + RTP_HEADER_LENGTH_BYTES, MBE_SAMPLES_LENGTH * 2U); } else { - ::memcpy(pcm, buffer + 4U, pcmLength); + if (m_udpNoIncludeLength) { + ::memcpy(pcm, buffer, pcmLength); + } + else { + ::memcpy(pcm, buffer + 4U, pcmLength); + } } // Utils::dump(1U, "PCM RECV BYTE BUFFER", pcm, pcmLength); @@ -1183,10 +1219,20 @@ void HostBridge::processUDPAudio() int smpIdx = 0; short samples[MBE_SAMPLES_LENGTH]; if (m_udpUseULaw) { - for (uint32_t pcmIdx = 0; pcmIdx < pcmLength; pcmIdx++) { - samples[smpIdx] = uLawToPCM(pcm[pcmIdx]); + if (m_trace) + Utils::dump(1U, "HostBridge()::processUDPAudio() uLaw Audio", pcm, MBE_SAMPLES_LENGTH * 2U); + + for (uint32_t pcmIdx = 0; pcmIdx < MBE_SAMPLES_LENGTH; pcmIdx++) { + samples[smpIdx] = decodeMuLaw(pcm[pcmIdx]); smpIdx++; } + + int pcmIdx = 0; + for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) { + pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF); + pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF); + pcmIdx += 2; + } } else { for (uint32_t pcmIdx = 0; pcmIdx < pcmLength; pcmIdx += 2) { @@ -1379,6 +1425,9 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length) m_rxDMRPILC = lc::PrivacyLC(); m_rxStartTime = 0U; m_rxStreamId = 0U; + + m_rtpSeqNo = 0U; + m_rtpTimestamp = INVALID_TS; return; } @@ -1474,8 +1523,11 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst uint8_t pcm[MBE_SAMPLES_LENGTH * 2U]; if (m_udpUseULaw) { for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) { - pcm[smpIdx] = pcmTouLaw(samples[smpIdx]); + pcm[smpIdx] = encodeMuLaw(samples[smpIdx]); } + + if (m_trace) + Utils::dump(1U, "HostBridge()::decodeDMRAudioFrame() Encoded uLaw Audio", pcm, MBE_SAMPLES_LENGTH); } else { for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) { @@ -1498,6 +1550,22 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst __SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U); ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH); } + + // are we sending RTP audio frames? + if (m_udpRTPFrames) { + uint8_t* rtpFrame = generateRTPHeaders(MBE_SAMPLES_LENGTH, m_rtpSeqNo); + if (rtpFrame != nullptr) { + length += RTP_HEADER_LENGTH_BYTES; + uint8_t* newAudioData = new uint8_t[length]; + ::memcpy(newAudioData, rtpFrame, RTP_HEADER_LENGTH_BYTES); + ::memcpy(newAudioData + RTP_HEADER_LENGTH_BYTES, audioData, MBE_SAMPLES_LENGTH); + delete[] audioData; + + audioData = newAudioData; + } + + m_rtpSeqNo++; + } } else { __SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U); @@ -1583,6 +1651,9 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ dmrData.setData(data); + LogMessage(LOG_HOST, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X", m_slot, + dmrLC.getSrcId(), dmrLC.getDstId(), dmrData.getFLCO()); + m_network->writeDMR(dmrData, false); m_txStreamId = m_network->getDMRStreamId(m_slot); @@ -1798,6 +1869,9 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length) m_rxP25LC = lc::LC(); m_rxStartTime = 0U; m_rxStreamId = 0U; + + m_rtpSeqNo = 0U; + m_rtpTimestamp = INVALID_TS; return; } @@ -2040,8 +2114,11 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI uint8_t pcm[MBE_SAMPLES_LENGTH * 2U]; if (m_udpUseULaw) { for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) { - pcm[smpIdx] = pcmTouLaw(samples[smpIdx]); + pcm[smpIdx] = encodeMuLaw(samples[smpIdx]); } + + if (m_trace) + Utils::dump(1U, "HostBridge()::decodeP25AudioFrame() Encoded uLaw Audio", pcm, MBE_SAMPLES_LENGTH); } else { for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) { @@ -2064,6 +2141,22 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI __SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U); ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH); } + + // are we sending RTP audio frames? + if (m_udpRTPFrames) { + uint8_t* rtpFrame = generateRTPHeaders(MBE_SAMPLES_LENGTH, m_rtpSeqNo); + if (rtpFrame != nullptr) { + length += RTP_HEADER_LENGTH_BYTES; + uint8_t* newAudioData = new uint8_t[length]; + ::memcpy(newAudioData, rtpFrame, RTP_HEADER_LENGTH_BYTES); + ::memcpy(newAudioData + RTP_HEADER_LENGTH_BYTES, audioData, MBE_SAMPLES_LENGTH); + delete[] audioData; + + audioData = newAudioData; + } + + m_rtpSeqNo++; + } } else { __SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U); @@ -2248,7 +2341,7 @@ void HostBridge::encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ m_p25N++; } -/* Helper to generate the preamble tone. */ +/* Helper to generate the single-tone preamble tone. */ void HostBridge::generatePreambleTone() { @@ -2260,6 +2353,8 @@ void HostBridge::generatePreambleTone() return; } + ma_waveform_set_frequency(&m_maSineWaveform, m_preambleTone); + ma_uint32 pcmBytes = frameCount * ma_get_bytes_per_frame(m_maDevice.capture.format, m_maDevice.capture.channels); UInt8Array __sine = std::make_unique(pcmBytes); uint8_t* sine = __sine.get(); @@ -2278,6 +2373,40 @@ void HostBridge::generatePreambleTone() m_outputAudio.addData(sineSamples, frameCount); } +/* Helper to generate outgoing RTP headers. */ + +uint8_t* HostBridge::generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq) +{ + uint32_t timestamp = m_rtpTimestamp; + if (timestamp != INVALID_TS) { + timestamp += (RTP_GENERIC_CLOCK_RATE / 50); + if (m_debug) + LogDebug(LOG_NET, "HostBridge::generateRTPHeaders() RTP, previous TS = %u, TS = %u, rtpSeq = %u", m_rtpTimestamp, timestamp, rtpSeq); + m_rtpTimestamp = timestamp; + } + + // generate RTP header + RTPHeader header = RTPHeader(); + + header.setPayloadType(RTP_G711_PAYLOAD_TYPE); + header.setTimestamp(timestamp); + header.setSequence(rtpSeq); + header.setSSRC(m_network->getPeerId()); + + uint8_t* buffer = new uint8_t[RTP_HEADER_LENGTH_BYTES + msgLen]; + ::memset(buffer, 0x00U, RTP_HEADER_LENGTH_BYTES + msgLen); + + header.encode(buffer); + + if (timestamp == INVALID_TS) { + if (m_debug) + LogDebug(LOG_NET, "HostBridge::generateRTPHeaders() RTP, initial TS = %u, rtpSeq = %u", header.getTimestamp(), rtpSeq); + m_rtpTimestamp = header.getTimestamp(); + } + + return buffer; +} + /* Helper to end a local or UDP call. */ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) @@ -2297,10 +2426,25 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) switch (m_txMode) { case TX_MODE_DMR: { + dmr::defines::DataType::E dataType = dmr::defines::DataType::VOICE_SYNC; + if (m_dmrN == 0) + dataType = dmr::defines::DataType::VOICE_SYNC; + else { + dataType = dmr::defines::DataType::VOICE; + } + dmr::data::NetData data = dmr::data::NetData(); - data.setDataType(dmr::defines::DataType::TERMINATOR_WITH_LC); - data.setDstId(dstId); + data.setSlotNo(m_slot); + data.setDataType(dataType); data.setSrcId(srcId); + data.setDstId(dstId); + data.setFLCO(dmr::defines::FLCO::GROUP); + data.setN(m_dmrN); + data.setSeqNo(m_dmrSeqNo); + data.setBER(0U); + data.setRSSI(0U); + + LogMessage(LOG_HOST, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot, dstId); m_network->writeDMRTerminator(data, &m_dmrSeqNo, &m_dmrN, m_dmrEmbeddedData); m_network->resetDMR(data.getSlotNo()); @@ -2315,6 +2459,8 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); + LogMessage(LOG_HOST, P25_TDU_STR); + uint8_t controlByte = 0x00U; m_network->writeP25TDU(lc, lsd, controlByte); m_network->resetP25(); @@ -2334,6 +2480,9 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) m_dmrN = 0U; m_p25SeqNo = 0U; m_p25N = 0U; + + m_rtpSeqNo = 0U; + m_rtpTimestamp = INVALID_TS; } /* Entry point to audio processing thread. */ diff --git a/src/bridge/HostBridge.h b/src/bridge/HostBridge.h index 675f8cfa..2bbf9da9 100644 --- a/src/bridge/HostBridge.h +++ b/src/bridge/HostBridge.h @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -96,25 +96,25 @@ void mdcPacketDetected(int frameCount, mdc_u8_t op, mdc_u8_t arg, mdc_u16_t unit * @param pcm PCM value. * @return uint8_t aLaw value. */ -uint8_t pcmToaLaw(short pcm); +uint8_t encodeALaw(short pcm); /** * @brief Helper to convert G.711 aLaw into PCM. * @param alaw aLaw value. * @return short PCM value. */ -short aLawToPCM(uint8_t alaw); +short decodeALaw(uint8_t alaw); /** - * @brief Helper to convert PCM into G.711 uLaw. + * @brief Helper to convert PCM into G.711 MuLaw. * @param pcm PCM value. - * @return uint8_t uLaw value. + * @return uint8_t MuLaw value. */ -uint8_t pcmTouLaw(short pcm); +uint8_t encodeMuLaw(short pcm); /** - * @brief Helper to convert G.711 uLaw into PCM. - * @param ulaw uLaw value. + * @brief Helper to convert G.711 MuLaw into PCM. + * @param ulaw MuLaw value. * @return short PCM value. */ -short uLawToPCM(uint8_t ulaw); +short decodeMuLaw(uint8_t ulaw); // --------------------------------------------------------------------------- // Class Declaration @@ -161,6 +161,7 @@ private: std::string m_udpReceiveAddress; bool m_udpNoIncludeLength; bool m_udpUseULaw; + bool m_udpRTPFrames; uint32_t m_srcId; uint32_t m_srcIdOverride; @@ -238,8 +239,12 @@ private: bool m_dumpSampleLevel; bool m_running; + bool m_trace; bool m_debug; + uint16_t m_rtpSeqNo; + uint32_t m_rtpTimestamp; + static std::mutex m_audioMutex; static std::mutex m_networkMutex; @@ -434,7 +439,15 @@ private: void encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId = 0U, uint32_t forcedDstId = 0U); /** - * @brief Helper to generate the preamble tone. + * @brief Helper to generate outgoing RTP headers. + * @param msgLen Message Length. + * @param rtpSeq RTP Sequence. + * @returns uint8_t* Buffer containing the encoded RTP headers. + */ + uint8_t* generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq); + + /** + * @brief Helper to generate the single-tone preamble tone. */ void generatePreambleTone(); diff --git a/src/common/nxdn/lc/RCCH.cpp b/src/common/nxdn/lc/RCCH.cpp index 04157c83..f409f42d 100644 --- a/src/common/nxdn/lc/RCCH.cpp +++ b/src/common/nxdn/lc/RCCH.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2022,204 Bryan Biedenkapp, N2PLL + * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL * */ #include "nxdn/NXDNDefines.h" diff --git a/src/common/p25/P25Utils.cpp b/src/common/p25/P25Utils.cpp index a9d57b36..e7b7c1ff 100644 --- a/src/common/p25/P25Utils.cpp +++ b/src/common/p25/P25Utils.cpp @@ -5,6 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2016 Jonathan Naylor, G4KLX + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 14cf9fe3..f7f95741 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -136,7 +136,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this the end of the call stream? if (dataSync && (dataType == DataType::TERMINATOR_WITH_LC)) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "DMR, invalid TERMINATOR, peer = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, srcId, dstId, streamId, external); + LogWarning(LOG_NET, "DMR, invalid TERMINATOR, peer = %u, srcId = %u, dstId = %u, slot = %u, streamId = %u, external = %u", peerId, srcId, dstId, slotNo, streamId, external); return false; } @@ -149,8 +149,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; }); if (it == m_status.end()) { - LogError(LOG_NET, "DMR, tried to end call for non-existent call in progress?, peer = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", - peerId, srcId, dstId, streamId, external); + LogError(LOG_NET, "DMR, tried to end call for non-existent call in progress?, peer = %u, srcId = %u, dstId = %u, slot = %u, streamId = %u, external = %u", + peerId, srcId, dstId, slotNo, streamId, external); } else { status = it->second; @@ -179,8 +179,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } } - LogMessage(LOG_NET, "DMR, Call End, peer = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, external = %u", - peerId, srcId, dstId, duration / 1000, streamId, external); + LogMessage(LOG_NET, "DMR, Call End, peer = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, external = %u", + peerId, srcId, dstId, slotNo, duration / 1000, streamId, external); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index cb8cf5c3..dd444613 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -494,6 +494,7 @@ bool Host::createModem() bool disableOFlowReset = modemConf["disableOFlowReset"].as(false); bool ignoreModemConfigArea = modemConf["ignoreModemConfigArea"].as(false); bool dumpModemStatus = modemConf["dumpModemStatus"].as(false); + bool respTrace = modemConf["respTrace"].as(false); bool trace = modemConf["trace"].as(false); bool debug = modemConf["debug"].as(false); @@ -687,6 +688,8 @@ bool Host::createModem() m_modem->setP25NAC(m_p25NAC); } + m_modem->setResponseTrace(respTrace); + if (m_modemRemote) { m_modem->setOpenHandler(MODEM_OC_PORT_HANDLER_BIND(Host::rmtPortModemOpen, this)); m_modem->setCloseHandler(MODEM_OC_PORT_HANDLER_BIND(Host::rmtPortModemClose, this)); diff --git a/src/host/dmr/lookups/DMRAffiliationLookup.h b/src/host/dmr/lookups/DMRAffiliationLookup.h index 0e394f12..cf5587cc 100644 --- a/src/host/dmr/lookups/DMRAffiliationLookup.h +++ b/src/host/dmr/lookups/DMRAffiliationLookup.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Modem Host Software - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/host/modem/Modem.cpp b/src/host/modem/Modem.cpp index c3fc90e4..53bbbd35 100644 --- a/src/host/modem/Modem.cpp +++ b/src/host/modem/Modem.cpp @@ -158,6 +158,7 @@ Modem::Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert, m_flashDisabled(false), m_gotModemStatus(false), m_dumpModemStatus(dumpModemStatus), + m_respTrace(false), m_trace(trace), m_debug(debug) { @@ -579,6 +580,8 @@ void Modem::clock(uint32_t ms) m_rxDMRQueue1.addData(&data, 1U); m_rxDMRQueue1.addData(m_buffer + 3U, m_length - 3U); + if (m_trace) + Utils::dump(1U, "Modem::clock() RX DMR Data 1", m_buffer + 3U, m_length - 3U); } } break; @@ -603,6 +606,8 @@ void Modem::clock(uint32_t ms) m_rxDMRQueue2.addData(&data, 1U); m_rxDMRQueue2.addData(m_buffer + 3U, m_length - 3U); + if (m_trace) + Utils::dump(1U, "Modem::clock() RX DMR Data 2", m_buffer + 3U, m_length - 3U); } } break; @@ -663,6 +668,8 @@ void Modem::clock(uint32_t ms) m_rxP25Queue.addData(&data, 1U); m_rxP25Queue.addData(m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U)); + if (m_trace) + Utils::dump(1U, "Modem::clock() RX P25 Data", m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U)); } } break; @@ -704,6 +711,8 @@ void Modem::clock(uint32_t ms) m_rxNXDNQueue.addData(&data, 1U); m_rxNXDNQueue.addData(m_buffer + 3U, m_length - 3U); + if (m_trace) + Utils::dump(1U, "Modem::clock() RX NXDN Data", m_buffer + 3U, m_length - 3U); } } break; @@ -1391,8 +1400,8 @@ bool Modem::writeDMRFrame1(const uint8_t* data, uint32_t length) if (m_dmrSpace1 >= length) { if (m_debug) LogDebug(LOG_MODEM, "Modem::writeDMRData1(); immediate write (len %u)", length); - //if (m_trace) - // Utils::dump(1U, "Immediate TX DMR Data 1", buffer, len); + if (m_trace) + Utils::dump(1U, "Modem::writeDMRData1() Immediate TX DMR Data 1", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1445,8 +1454,8 @@ bool Modem::writeDMRFrame2(const uint8_t* data, uint32_t length) if (m_dmrSpace2 >= length) { if (m_debug) LogDebug(LOG_MODEM, "Modem::writeDMRData2(); immediate write (len %u)", length); - //if (m_trace) - // Utils::dump(1U, "Immediate TX DMR Data 2", buffer, len); + if (m_trace) + Utils::dump(1U, "Modem::writeDMRData2() Immediate TX DMR Data 2", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1511,8 +1520,8 @@ bool Modem::writeP25Frame(const uint8_t* data, uint32_t length) if (m_p25Space >= length) { if (m_debug) LogDebug(LOG_MODEM, "Modem::writeP25Data(); immediate write (len %u)", length); - //if (m_trace) - // Utils::dump(1U, "Immediate TX P25 Data", buffer, len); + if (m_trace) + Utils::dump(1U, "Modem::writeP25Data() Immediate TX P25 Data", buffer + 3U, length - 3U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1565,8 +1574,8 @@ bool Modem::writeNXDNFrame(const uint8_t* data, uint32_t length) if (m_nxdnSpace >= length) { if (m_debug) LogDebug(LOG_MODEM, "Modem::writeNXDNData(); immediate write (len %u)", length); - //if (m_trace) - // Utils::dump(1U, "Immediate TX NXDN Data", buffer, len); + if (m_trace) + Utils::dump(1U, "Modem::writeNXDNData() Immediate TX NXDN Data", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1749,8 +1758,8 @@ bool Modem::sendCWId(const std::string& callsign) for (uint32_t i = 0U; i < length; i++) buffer[i + 3U] = callsign.at(i); - //if (m_trace) - // Utils::dump(1U, "CW ID Data", buffer, length + 3U); + if (m_trace) + Utils::dump(1U, "CW ID Data", buffer, length + 3U); return write(buffer, length + 3U) == int(length + 3U); } @@ -2324,36 +2333,36 @@ void Modem::printDebug(const uint8_t* buffer, uint16_t len) } if (buffer[2U] == CMD_DEBUG1) { - LogDebug(LOG_MODEM, "M: %.*s", len - 3U, buffer + 3U); + LogDebug(LOG_MODEM, "DSP_FW_API %.*s", len - 3U, buffer + 3U); } else if (buffer[2U] == CMD_DEBUG2) { short val1 = (buffer[len - 2U] << 8) | buffer[len - 1U]; - LogDebug(LOG_MODEM, "M: %.*s %X", len - 5U, buffer + 3U, val1); + LogDebug(LOG_MODEM, "DSP_FW_API %.*s %X", len - 5U, buffer + 3U, val1); } else if (buffer[2U] == CMD_DEBUG3) { short val1 = (buffer[len - 4U] << 8) | buffer[len - 3U]; short val2 = (buffer[len - 2U] << 8) | buffer[len - 1U]; - LogDebug(LOG_MODEM, "M: %.*s %X %X", len - 7U, buffer + 3U, val1, val2); + LogDebug(LOG_MODEM, "DSP_FW_API %.*s %X %X", len - 7U, buffer + 3U, val1, val2); } else if (buffer[2U] == CMD_DEBUG4) { short val1 = (buffer[len - 6U] << 8) | buffer[len - 5U]; short val2 = (buffer[len - 4U] << 8) | buffer[len - 3U]; short val3 = (buffer[len - 2U] << 8) | buffer[len - 1U]; - LogDebug(LOG_MODEM, "M: %.*s %X %X %X", len - 9U, buffer + 3U, val1, val2, val3); + LogDebug(LOG_MODEM, "DSP_FW_API %.*s %X %X %X", len - 9U, buffer + 3U, val1, val2, val3); } else if (buffer[2U] == CMD_DEBUG5) { short val1 = (buffer[len - 8U] << 8) | buffer[len - 7U]; short val2 = (buffer[len - 6U] << 8) | buffer[len - 5U]; short val3 = (buffer[len - 4U] << 8) | buffer[len - 3U]; short val4 = (buffer[len - 2U] << 8) | buffer[len - 1U]; - LogDebug(LOG_MODEM, "M: %.*s %X %X %X %X", len - 11U, buffer + 3U, val1, val2, val3, val4); + LogDebug(LOG_MODEM, "DSP_FW_API %.*s %X %X %X %X", len - 11U, buffer + 3U, val1, val2, val3, val4); } else if (buffer[2U] == CMD_DEBUG_DUMP) { uint8_t data[255U]; ::memset(data, 0x00U, 255U); ::memcpy(data, buffer, len); - Utils::dump(1U, "Modem Debug Dump", data, len); + Utils::dump(1U, "Modem::printDebug() DSP_FW_API Debug Dump", data, len); } } @@ -2363,7 +2372,7 @@ RESP_TYPE_DVM Modem::getResponse() { m_rspDoubleLength = false; - //LogDebug(LOG_MODEM, "getResponse(), checking if we have data"); + //LogDebug(LOG_MODEM, "Modem::getResponse(), checking if we have data"); // get the start of the frame or nothing at all if (m_rspState == RESP_START) { @@ -2375,7 +2384,7 @@ RESP_TYPE_DVM Modem::getResponse() } if (ret == 0) { - //LogDebug(LOG_MODEM, "getResponse(), no data available"); + //LogDebug(LOG_MODEM, "Modem::getResponse(), no data available"); return RTM_TIMEOUT; } @@ -2391,12 +2400,12 @@ RESP_TYPE_DVM Modem::getResponse() m_rspDoubleLength = true; } - //LogDebug(LOG_MODEM, "getResponse(), RESP_START"); + //LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_START"); m_rspState = RESP_LENGTH1; } - //LogDebug(LOG_MODEM, "getResponse(), getting frame length 1/2, rspDoubleLength = %u", m_rspDoubleLength); + //LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame length 1/2, rspDoubleLength = %u", m_rspDoubleLength); // get the length of the frame, 1/2 if (m_rspState == RESP_LENGTH1) { int ret = m_port->read(m_buffer + 1U, 1U); @@ -2423,12 +2432,12 @@ RESP_TYPE_DVM Modem::getResponse() m_length = m_buffer[1U]; } - //LogDebug(LOG_MODEM, "getResponse(), RESP_LENGTH1, len = %u", m_length); + //LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_LENGTH1, len = %u", m_length); m_rspOffset = 2U; } - //LogDebug(LOG_MODEM, "getResponse(), getting frame length 2/2"); + //LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame length 2/2"); // get the length of the frame, 2/2 if (m_rspState == RESP_LENGTH2) { int ret = m_port->read(m_buffer + 2U, 1U); @@ -2444,13 +2453,13 @@ RESP_TYPE_DVM Modem::getResponse() m_length = (m_length + (m_buffer[2U] & 0xFFU)); m_rspState = RESP_TYPE; - //LogDebug(LOG_MODEM, "getResponse(), RESP_LENGTH2, len = %u", m_length); + //LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_LENGTH2, len = %u", m_length); m_rspDoubleLength = true; m_rspOffset = 3U; } - //LogDebug(LOG_MODEM, "getResponse(), getting frame type"); + //LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame type"); // get the frame type if (m_rspState == RESP_TYPE) { int ret = m_port->read(m_buffer + m_rspOffset, 1U); @@ -2465,18 +2474,18 @@ RESP_TYPE_DVM Modem::getResponse() m_rspType = (DVM_COMMANDS)m_buffer[m_rspOffset]; - //LogDebug(LOG_MODEM, "getResponse(), RESP_TYPE, len = %u, type = %u", m_length, m_rspType); + //LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_TYPE, len = %u, type = %u", m_length, m_rspType); m_rspState = RESP_DATA; m_rspOffset++; } - //LogDebug(LOG_MODEM, "getResponse(), getting frame data"); + //LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame data"); // get the frame data if (m_rspState == RESP_DATA) { - if (m_debug && m_trace) - LogDebug(LOG_MODEM, "getResponse(), RESP_DATA, len = %u, offset = %u, type = %02X", m_length, m_rspOffset, m_rspType); + if (m_respTrace) + LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_DATA, len = %u, offset = %u, type = %02X", m_length, m_rspOffset, m_rspType); while (m_rspOffset < m_length) { int ret = m_port->read(m_buffer + m_rspOffset, m_length - m_rspOffset); @@ -2493,8 +2502,8 @@ RESP_TYPE_DVM Modem::getResponse() m_rspOffset += ret; } - if (m_debug && m_trace) - Utils::dump(1U, "Modem getResponse()", m_buffer, m_length); + if (m_respTrace) + Utils::dump(1U, "Modem::getResponse() Buffer", m_buffer, m_length); } m_rspState = RESP_START; diff --git a/src/host/modem/Modem.h b/src/host/modem/Modem.h index fb1728dd..93e6711f 100644 --- a/src/host/modem/Modem.h +++ b/src/host/modem/Modem.h @@ -897,6 +897,10 @@ namespace modem std::string rsnToString(uint8_t reason); public: + /** + * @brief Flag indicating if modem response trace is enabled. + */ + __PROTECTED_PROPERTY(bool, respTrace, ResponseTrace); /** * @brief Flag indicating if modem trace is enabled. */ diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 7d7deba3..6a355ac8 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2024-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2024 Patrick McDonnell, W3AXL * */ @@ -198,6 +198,8 @@ void ModemV24::clock(uint32_t ms) convertToAirTIA(m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U)); else convertToAir(m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U)); + if (m_trace) + Utils::dump(1U, "ModemV24::clock() RX P25 Data", m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U)); } } break; diff --git a/src/host/modem/port/specialized/V24UDPPort.cpp b/src/host/modem/port/specialized/V24UDPPort.cpp index fc3f2018..c4776305 100644 --- a/src/host/modem/port/specialized/V24UDPPort.cpp +++ b/src/host/modem/port/specialized/V24UDPPort.cpp @@ -840,6 +840,8 @@ void V24UDPPort::getStatus() reply[3U] = 0x00U; reply[3U] |= 0x08U; // P25 enable flag + if (m_fscState == CS_CONNECTED) + reply[3U] |= 0x40U; // V.24 connected flag reply[4U] = uint8_t(m_modemState); diff --git a/src/host/network/Network.h b/src/host/network/Network.h index a5e9f297..18182782 100644 --- a/src/host/network/Network.h +++ b/src/host/network/Network.h @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Modem Host Software -* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX * Copyright (C) 2017-2023 Bryan Biedenkapp, N2PLL * diff --git a/src/host/nxdn/packet/Data.h b/src/host/nxdn/packet/Data.h index ee375857..70607942 100644 --- a/src/host/nxdn/packet/Data.h +++ b/src/host/nxdn/packet/Data.h @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Modem Host Software - * @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2015-2020 Jonathan Naylor, G4KLX * Copyright (C) 2022-2024 Bryan Biedenkapp, N2PLL * diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 172f61c0..d33d7d39 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2016,2017,2018 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -78,6 +78,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q m_convNetGrantDemand(false), m_sndcpSupport(false), m_ignoreAffiliationCheck(false), + m_demandUnitRegForRefusedAff(true), m_idenTable(idenTable), m_ridLookup(ridLookup), m_tidLookup(tidLookup), @@ -267,6 +268,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw m_sndcpSupport = p25Protocol["sndcpSupport"].as(false); m_ignoreAffiliationCheck = p25Protocol["ignoreAffiliationCheck"].as(false); + m_demandUnitRegForRefusedAff = p25Protocol["demandUnitRegForRefusedAff"].as(true); yaml::Node control = p25Protocol["control"]; m_enableControl = control["enable"].as(false); @@ -493,6 +495,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogInfo(" Unit-to-Unit Availability Check: %s", m_control->m_unitToUnitAvailCheck ? "yes" : "no"); LogInfo(" Explicit Source ID Support: %s", m_allowExplicitSourceId ? "yes" : "no"); LogInfo(" Conventional Network Grant Demand: %s", m_convNetGrantDemand ? "yes" : "no"); + LogInfo(" Demand Unit Registration for Refused Affiliation: %s", m_demandUnitRegForRefusedAff ? "yes" : "no"); if (disableUnitRegTimeout) { LogInfo(" Disable Unit Registration Timeout: yes"); diff --git a/src/host/p25/Control.h b/src/host/p25/Control.h index 47c2ebef..6b0d7a0c 100644 --- a/src/host/p25/Control.h +++ b/src/host/p25/Control.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2016,2017 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -319,6 +319,7 @@ namespace p25 bool m_convNetGrantDemand; bool m_sndcpSupport; bool m_ignoreAffiliationCheck; + bool m_demandUnitRegForRefusedAff; ::lookups::IdenTableLookup* m_idenTable; ::lookups::RadioIdLookup* m_ridLookup; diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp index 9cd38c29..1e9f36f2 100644 --- a/src/host/p25/packet/ControlSignaling.cpp +++ b/src/host/p25/packet/ControlSignaling.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2022 Jason-UWU * */ @@ -540,7 +540,11 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptrm_demandUnitRegForRefusedAff) { + writeRF_TSDU_U_Reg_Cmd(srcId); + } + } } break; case TSBKO::ISP_GRP_AFF_Q_RSP: @@ -1153,7 +1157,7 @@ void ControlSignaling::writeRF_TSDU_Ext_Func(uint32_t func, uint32_t arg, uint32 break; } - writeRF_TSDU_SBF_Imm(iosp.get(), false); + writeRF_TSDU_SBF_Imm(iosp.get(), true); } /* Helper to write a group affiliation query packet. */ @@ -2637,10 +2641,8 @@ void ControlSignaling::writeRF_TSDU_Deny(uint32_t srcId, uint32_t dstId, uint8_t /* Helper to write a group affiliation response packet. */ -bool ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) +uint8_t ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) { - bool ret = false; - std::unique_ptr iosp = std::make_unique(); iosp->setMFId(m_lastMFID); iosp->setAnnounceGroup(m_announcementGroup); @@ -2714,7 +2716,6 @@ bool ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) } ::ActivityLog("P25", true, "group affiliation request from %u to %s %u", srcId, "TG ", dstId); - ret = true; // update dynamic affiliation table m_p25->m_affiliations.groupAff(srcId, dstId); @@ -2724,7 +2725,7 @@ bool ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) } writeRF_TSDU_SBF_Imm(iosp.get(), noNet); - return ret; + return iosp->getResponse(); } /* Helper to write a unit registration response packet. */ diff --git a/src/host/p25/packet/ControlSignaling.h b/src/host/p25/packet/ControlSignaling.h index 62815caa..ce19d6ca 100644 --- a/src/host/p25/packet/ControlSignaling.h +++ b/src/host/p25/packet/ControlSignaling.h @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -364,8 +364,9 @@ namespace p25 * @brief Helper to write a group affiliation response packet. * @param srcId Source Radio ID. * @param dstId Destination ID. + * @returns uint8_t Grant response code. */ - bool writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId); + uint8_t writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId); /** * @brief Helper to write a unit registration response packet. * @param srcId Source Radio ID. diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index 56f763ff..c1bba2d3 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -409,7 +409,7 @@ bool Voice::process(uint8_t* data, uint32_t len) // are we auto-registering legacy radios to groups? if (m_p25->m_legacyGroupReg && group) { if (!m_p25->m_affiliations.isGroupAff(srcId, dstId)) { - if (!m_p25->m_control->writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId)) { + if (m_p25->m_control->writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId) != ResponseCode::ACCEPT) { LogWarning(LOG_RF, P25_HDU_STR " denial, conventional affiliation required, not affiliated to TGID, srcId = %u, dstId = %u", srcId, dstId); m_p25->m_rfLastDstId = 0U; m_p25->m_rfLastSrcId = 0U; diff --git a/src/remote/RESTClient.h b/src/remote/RESTClient.h index 611156b5..87706491 100644 --- a/src/remote/RESTClient.h +++ b/src/remote/RESTClient.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Remote Command Client -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * */