Add support for USRP (#83)

* Add support for USRP UDP transport

* Maintain USRP sequence

* Oops not sure how I did that

* Update DMR to be like P25

* Check for invalid UDP configuration

---------

Co-authored-by: firealarmss <caleb.k4php@gmail.com>
4.11f_maint
firealarmss 11 months ago committed by GitHub
parent db1d000b21
commit aedabceac7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -76,6 +76,8 @@ network:
# Flag indicating UDP audio should be RTP framed. # Flag indicating UDP audio should be RTP framed.
# NOTE: This flag is only applicable when encoding G.711 uLaw. # NOTE: This flag is only applicable when encoding G.711 uLaw.
udpRTPFrames: false udpRTPFrames: false
# Flag indicating UDP audio should follow the USRP format.
udpUsrp: false
# Source "Radio ID" for transmitted audio frames. # Source "Radio ID" for transmitted audio frames.
sourceId: 1234567 sourceId: 1234567

@ -5,6 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2025 Caleb, K4PHP
* *
*/ */
#include "Defines.h" #include "Defines.h"
@ -293,6 +294,7 @@ HostBridge::HostBridge(const std::string& confFile) :
m_udpNoIncludeLength(false), m_udpNoIncludeLength(false),
m_udpUseULaw(false), m_udpUseULaw(false),
m_udpRTPFrames(false), m_udpRTPFrames(false),
m_udpUsrp(false),
m_srcId(p25::defines::WUID_FNE), m_srcId(p25::defines::WUID_FNE),
m_srcIdOverride(0U), m_srcIdOverride(0U),
m_overrideSrcIdFromMDC(false), m_overrideSrcIdFromMDC(false),
@ -353,7 +355,8 @@ HostBridge::HostBridge(const std::string& confFile) :
m_trace(false), m_trace(false),
m_debug(false), m_debug(false),
m_rtpSeqNo(0U), m_rtpSeqNo(0U),
m_rtpTimestamp(INVALID_TS) m_rtpTimestamp(INVALID_TS),
m_usrpSeqNo(0U)
#if defined(_WIN32) #if defined(_WIN32)
, ,
m_encoderState(nullptr), m_encoderState(nullptr),
@ -1019,10 +1022,19 @@ bool HostBridge::createNetwork()
m_udpReceivePort = (uint16_t)networkConf["udpReceivePort"].as<uint32_t>(34001); m_udpReceivePort = (uint16_t)networkConf["udpReceivePort"].as<uint32_t>(34001);
m_udpReceiveAddress = networkConf["udpReceiveAddress"].as<std::string>(); m_udpReceiveAddress = networkConf["udpReceiveAddress"].as<std::string>();
m_udpUseULaw = networkConf["udpUseULaw"].as<bool>(false); m_udpUseULaw = networkConf["udpUseULaw"].as<bool>(false);
m_udpUsrp = networkConf["udpUsrp"].as<bool>(false);
if (m_udpUsrp) {
m_udpMetadata = false; // USRP disables metadata due to USRP always having metadata
m_udpRTPFrames = false; // USRP disables RTP
m_udpNoIncludeLength = true; // USRP disables length
m_udpUseULaw = false; // USRP disables ULaw
}
if (m_udpUseULaw) { if (m_udpUseULaw) {
m_udpNoIncludeLength = networkConf["udpNoIncludeLength"].as<bool>(false); m_udpNoIncludeLength = networkConf["udpNoIncludeLength"].as<bool>(false);
m_udpRTPFrames = networkConf["udpRTPFrames"].as<bool>(false); m_udpRTPFrames = networkConf["udpRTPFrames"].as<bool>(false);
m_udpUsrp = false; // ULaw disables USRP
if (m_udpRTPFrames) if (m_udpRTPFrames)
m_udpNoIncludeLength = true; // RTP disables the length being included m_udpNoIncludeLength = true; // RTP disables the length being included
} }
@ -1096,6 +1108,7 @@ bool HostBridge::createNetwork()
LogInfo(" UDP Audio No Length Header: %s", 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"); LogInfo(" UDP Audio RTP Framed: %s", m_udpRTPFrames ? "yes" : "no");
} }
LogInfo(" UDP Audio USRP: %s", m_udpUsrp ? "yes" : "no");
} }
LogInfo(" Source ID: %u", m_srcId); LogInfo(" Source ID: %u", m_srcId);
@ -1178,12 +1191,13 @@ void HostBridge::processUDPAudio()
pcmLength = __GET_UINT32(buffer, 0U); pcmLength = __GET_UINT32(buffer, 0U);
} }
if (m_udpRTPFrames) if (m_udpRTPFrames || m_udpUsrp)
pcmLength = MBE_SAMPLES_LENGTH * 2U; pcmLength = MBE_SAMPLES_LENGTH * 2U;
UInt8Array __pcm = std::make_unique<uint8_t[]>(pcmLength); UInt8Array __pcm = std::make_unique<uint8_t[]>(pcmLength);
uint8_t* pcm = __pcm.get(); uint8_t* pcm = __pcm.get();
if (!m_udpUsrp) {
if (m_udpRTPFrames) { if (m_udpRTPFrames) {
RTPHeader rtpHeader = RTPHeader(); RTPHeader rtpHeader = RTPHeader();
rtpHeader.decode(buffer); rtpHeader.decode(buffer);
@ -1203,6 +1217,14 @@ void HostBridge::processUDPAudio()
::memcpy(pcm, buffer + 4U, pcmLength); ::memcpy(pcm, buffer + 4U, pcmLength);
} }
} }
}
else {
uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH];
::memcpy(usrpHeader, buffer, USRP_HEADER_LENGTH);
if (usrpHeader[15U] == 1U && length > USRP_HEADER_LENGTH) // PTT state true and ensure we did not just receive a USRP header
::memcpy(pcm, buffer + USRP_HEADER_LENGTH, pcmLength);
}
// Utils::dump(1U, "PCM RECV BYTE BUFFER", pcm, pcmLength); // Utils::dump(1U, "PCM RECV BYTE BUFFER", pcm, pcmLength);
@ -1444,6 +1466,10 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length)
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
uint64_t diff = now - m_rxStartTime; uint64_t diff = now - m_rxStartTime;
// send USRP end of transmission
if (m_udpUsrp)
sendUsrpEot();
LogMessage(LOG_HOST, "P25, call end (T), srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); LogMessage(LOG_HOST, "P25, call end (T), srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U);
} }
@ -1539,6 +1565,7 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst
uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U; uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U;
uint8_t* audioData = nullptr; uint8_t* audioData = nullptr;
if (!m_udpUsrp) {
if (!m_udpMetadata) { if (!m_udpMetadata) {
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length) audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length)
if (m_udpUseULaw) { if (m_udpUseULaw) {
@ -1546,7 +1573,8 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst
if (m_udpNoIncludeLength) { if (m_udpNoIncludeLength) {
length = MBE_SAMPLES_LENGTH; length = MBE_SAMPLES_LENGTH;
::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH); ::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH);
} else { }
else {
__SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U); __SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH); ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH);
} }
@ -1582,6 +1610,21 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst
__SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U)); __SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U));
__SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U)); __SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U));
} }
}
else {
uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH];
length = (MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH;
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH]; // PCM + 32 bytes (USRP Header)
m_usrpSeqNo++;
usrpHeader[15U] = 1; // set PTT state to true
__SET_UINT32(m_usrpSeqNo, usrpHeader, 4U);
::memcpy(usrpHeader, "USRP", 4);
::memcpy(audioData, usrpHeader, USRP_HEADER_LENGTH); // copy USRP header into the UDP payload
::memcpy(audioData + USRP_HEADER_LENGTH, pcm, MBE_SAMPLES_LENGTH * 2U);
}
sockaddr_storage addr; sockaddr_storage addr;
uint32_t addrLen; uint32_t addrLen;
@ -1863,6 +1906,11 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length)
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
uint64_t diff = now - m_rxStartTime; uint64_t diff = now - m_rxStartTime;
// send USRP end of transmission
if (m_udpUsrp) {
sendUsrpEot();
}
LogMessage(LOG_HOST, "P25, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); LogMessage(LOG_HOST, "P25, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U);
} }
@ -2130,6 +2178,8 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI
uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U; uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U;
uint8_t* audioData = nullptr; uint8_t* audioData = nullptr;
if (!m_udpUsrp) {
if (!m_udpMetadata) { if (!m_udpMetadata) {
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length) audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length)
if (m_udpUseULaw) { if (m_udpUseULaw) {
@ -2137,7 +2187,8 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI
if (m_udpNoIncludeLength) { if (m_udpNoIncludeLength) {
length = MBE_SAMPLES_LENGTH; length = MBE_SAMPLES_LENGTH;
::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH); ::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH);
} else { }
else {
__SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U); __SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH); ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH);
} }
@ -2173,6 +2224,21 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI
__SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U)); __SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U));
__SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U)); __SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U));
} }
}
else {
uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH];
length = (MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH;
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH]; // PCM + 32 bytes (USRP Header)
m_usrpSeqNo++;
usrpHeader[15U] = 1; // set PTT state to true
__SET_UINT32(m_usrpSeqNo, usrpHeader, 4U);
::memcpy(usrpHeader, "USRP", 4);
::memcpy(audioData, usrpHeader, USRP_HEADER_LENGTH); // copy USRP header into the UDP payload
::memcpy(audioData + USRP_HEADER_LENGTH, pcm, MBE_SAMPLES_LENGTH * 2U);
}
sockaddr_storage addr; sockaddr_storage addr;
uint32_t addrLen; uint32_t addrLen;
@ -2341,6 +2407,22 @@ void HostBridge::encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_
m_p25N++; m_p25N++;
} }
/* Helper to send USRP end of transmission */
void HostBridge::sendUsrpEot() {
sockaddr_storage addr;
uint32_t addrLen;
uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH];
m_usrpSeqNo = 0U;
::memcpy(usrpHeader, "USRP", 4);
if (udp::Socket::lookup(m_udpSendAddress, m_udpSendPort, addr, addrLen) == 0) {
m_udpAudioSocket->write(usrpHeader, USRP_HEADER_LENGTH, addr, addrLen);
}
}
/* Helper to generate the single-tone preamble tone. */ /* Helper to generate the single-tone preamble tone. */
void HostBridge::generatePreambleTone() void HostBridge::generatePreambleTone()

@ -56,6 +56,8 @@
#define DECSTATE_SIZE 2048 #define DECSTATE_SIZE 2048
#define ENCSTATE_SIZE 6144 #define ENCSTATE_SIZE 6144
#define USRP_HEADER_LENGTH 32
const uint8_t FULL_RATE_MODE = 0x00U; const uint8_t FULL_RATE_MODE = 0x00U;
const uint8_t HALF_RATE_MODE = 0x01U; const uint8_t HALF_RATE_MODE = 0x01U;
@ -162,6 +164,7 @@ private:
bool m_udpNoIncludeLength; bool m_udpNoIncludeLength;
bool m_udpUseULaw; bool m_udpUseULaw;
bool m_udpRTPFrames; bool m_udpRTPFrames;
bool m_udpUsrp;
uint32_t m_srcId; uint32_t m_srcId;
uint32_t m_srcIdOverride; uint32_t m_srcIdOverride;
@ -245,6 +248,8 @@ private:
uint16_t m_rtpSeqNo; uint16_t m_rtpSeqNo;
uint32_t m_rtpTimestamp; uint32_t m_rtpTimestamp;
uint32_t m_usrpSeqNo;
static std::mutex m_audioMutex; static std::mutex m_audioMutex;
static std::mutex m_networkMutex; static std::mutex m_networkMutex;
@ -446,6 +451,11 @@ private:
*/ */
uint8_t* generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq); uint8_t* generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq);
/**
* @brief Helper to generate USRP end of transmission
*/
void sendUsrpEot();
/** /**
* @brief Helper to generate the single-tone preamble tone. * @brief Helper to generate the single-tone preamble tone.
*/ */

Loading…
Cancel
Save

Powered by TurnKey Linux.