begin some corrective work for bridge NXDN support;

r05a06_dev
Bryan Biedenkapp 3 days ago
parent a2dea3c4c6
commit 44b5042c35

@ -142,6 +142,9 @@ void HostBridge::processNXDNNetwork(uint8_t* buffer, uint32_t length)
m_rxNXDNLC = lc::RTCH();
m_rxStartTime = 0U;
m_rxStreamId = 0U;
m_nxdnSeqNo = 0U;
m_nxdnN = 0U;
::memset(m_nxdnAMBE, 0x00U, 36U);
if (!m_udpRTPContinuousSeq) {
m_rtpInitialFrame = false;
@ -157,6 +160,26 @@ void HostBridge::processNXDNNetwork(uint8_t* buffer, uint32_t length)
if (m_ignoreCall)
return;
// Only superframe SACCH traffic frames contain voice payloads.
if (fct != FuncChannelType::USC_SACCH_SS)
return;
// A bridge may join an already active stream after its VCALL header. Treat
// the first valid voice frame as the start of the receive call as well.
if (!m_callInProgress) {
m_callInProgress = true;
m_callAlgoId = 0U;
m_networkWatchdog.start();
m_rxStartTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
LogInfoEx(LOG_HOST, "NXDN, late entry call start, srcId = %u, dstId = %u", srcId, dstId);
if (m_preambleLeaderTone)
generatePreambleTone();
}
else {
m_networkWatchdog.start();
}
LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u", srcId, dstId);
decodeNXDNAudioFrame(frame, srcId, dstId, m_nxdnSeqNo);
@ -187,39 +210,52 @@ void HostBridge::decodeNXDNAudioFrame(uint8_t* frame, uint32_t srcId, uint32_t d
::memset(nxdnAMBE, 0x00U, 18U);
::memcpy(nxdnAMBE, frame + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + pairOffset, 18U);
ambeFec.regenerateNXDN(nxdnAMBE + 0U);
ambeFec.regenerateNXDN(nxdnAMBE + 9U);
const uint32_t fecErrors[] = {
ambeFec.regenerateNXDN(nxdnAMBE + 0U),
ambeFec.regenerateNXDN(nxdnAMBE + 9U)
};
// match the normal NXDN voice path's lost-audio policy -- a pair is
// half of a full four-codeword frame, so use half the frame threshold
if ((fecErrors[0U] + fecErrors[1U]) > (DEFAULT_SILENCE_THRESHOLD / 2U)) {
::memcpy(nxdnAMBE + 0U, NULL_AMBE, 9U);
::memcpy(nxdnAMBE + 9U, NULL_AMBE, 9U);
LogWarning(LOG_HOST, "NXDN, AMBE errors exceeded threshold, substituting silence, errors = %u",
fecErrors[0U] + fecErrors[1U]);
}
uint8_t packedBits[13U];
::memset(packedBits, 0x00U, 13U);
nxdnAudio.decode(nxdnAMBE, packedBits);
for (uint8_t half = 0U; half < 2U; half++) {
uint8_t rawBits[72U];
::memset(rawBits, 0x00U, 72U);
uint8_t rawBits[49U];
::memset(rawBits, 0x00U, sizeof(rawBits));
for (uint32_t b = 0U; b < 49U; b++) {
rawBits[b] = READ_BIT(packedBits, (half * 49U) + b) ? 1U : 0U;
}
// HACK: use the DMR AMBE handling to decode NXDN audio
uint8_t dmrAMBE[dmr::defines::DMR_AMBE_LENGTH_BYTES];
::memset(dmrAMBE, 0x00U, dmr::defines::DMR_AMBE_LENGTH_BYTES);
m_encoder->encodeBits(rawBits, dmrAMBE);
short samples[AUDIO_SAMPLES_LENGTH];
int errs = 0;
#if defined(_WIN32)
if (m_useExternalVocoder) {
ambeDecode(dmrAMBE, dmr::defines::DMR_AMBE_LENGTH_BYTES, samples);
// reframe the NXDN 49-bit payload before handing it to the external vocoder
uint8_t ambePartial[dmr::defines::RAW_AMBE_LENGTH_BYTES];
::memset(ambePartial, 0x00U, sizeof(ambePartial));
m_encoder->encodeBits(rawBits, ambePartial);
errs = ambeDecode(ambePartial, dmr::defines::RAW_AMBE_LENGTH_BYTES, samples);
}
else {
#endif // defined(_WIN32)
errs = m_decoder->decode(dmrAMBE, samples);
errs = m_decoder->decodeBits(rawBits, samples);
#if defined(_WIN32)
}
#endif // defined(_WIN32)
errs += (int)fecErrors[half];
if (m_debug) {
LogDebug(LOG_HOST, "NXDN, Frame, VC%u.%u, srcId = %u, dstId = %u, errs = %u", nxdnN, vcBase + half, srcId, dstId, errs);
}
@ -306,16 +342,25 @@ void HostBridge::encodeNXDNAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32
AnalogAudio::gain(samples, AUDIO_SAMPLES_LENGTH, m_txAudioGain);
// HACK: use the DMR AMBE handling to encode NXDN audio
uint8_t dmrAMBE[dmr::defines::DMR_AMBE_LENGTH_BYTES];
::memset(dmrAMBE, 0x00U, dmr::defines::DMR_AMBE_LENGTH_BYTES);
uint8_t rawBits[49U];
::memset(rawBits, 0x00U, sizeof(rawBits));
#if defined(_WIN32)
if (m_useExternalVocoder) {
ambeEncode(samples, AUDIO_SAMPLES_LENGTH, dmrAMBE);
// reframe the NXDN 49-bit payload before handing it to the external vocoder
uint8_t ambePartial[dmr::defines::RAW_AMBE_LENGTH_BYTES];
::memset(ambePartial, 0x00U, sizeof(ambePartial));
ambeEncode(samples, AUDIO_SAMPLES_LENGTH, ambePartial);
char mbeBits[49U];
::memset(mbeBits, 0x00U, sizeof(mbeBits));
m_decoder->decodeBits(ambePartial, mbeBits);
for (uint32_t i = 0U; i < 49U; i++)
rawBits[i] = mbeBits[i] != 0;
}
else {
#endif // defined(_WIN32)
m_encoder->encode(samples, dmrAMBE);
m_encoder->encodeBits(samples, rawBits);
#if defined(_WIN32)
}
#endif // defined(_WIN32)
@ -324,7 +369,10 @@ void HostBridge::encodeNXDNAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32
m_nxdnN = 0U;
}
::memcpy(m_nxdnAMBE + (m_nxdnN * dmr::defines::DMR_AMBE_LENGTH_BYTES), dmrAMBE, dmr::defines::DMR_AMBE_LENGTH_BYTES);
uint8_t* packedAMBE = m_nxdnAMBE + (m_nxdnN * dmr::defines::DMR_AMBE_LENGTH_BYTES);
::memset(packedAMBE, 0x00U, dmr::defines::DMR_AMBE_LENGTH_BYTES);
for (uint32_t b = 0U; b < 49U; b++)
WRITE_BIT(packedAMBE, b, rawBits[b] != 0U);
m_nxdnN++;
if (m_nxdnN < 4U) {
@ -341,14 +389,9 @@ void HostBridge::encodeNXDNAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32
::memset(packedBits, 0x00U, 13U);
for (uint8_t half = 0U; half < 2U; half++) {
char mbeBits[49U];
::memset(mbeBits, 0x00U, 49U);
uint8_t* ambe = m_nxdnAMBE + ((pair * 2U + half) * dmr::defines::DMR_AMBE_LENGTH_BYTES);
m_decoder->decodeBits(ambe, mbeBits);
for (uint32_t b = 0U; b < 49U; b++) {
WRITE_BIT(packedBits, (half * 49U) + b, mbeBits[b] != 0);
WRITE_BIT(packedBits, (half * 49U) + b, READ_BIT(ambe, b));
}
}

@ -18,6 +18,7 @@
#include "common/dmr/data/EMB.h"
#include "common/nxdn/NXDNDefines.h"
#include "common/nxdn/NXDNUtils.h"
#include "common/nxdn/Audio.h"
#include "common/nxdn/Sync.h"
#include "common/nxdn/channel/FACCH1.h"
#include "common/nxdn/channel/LICH.h"
@ -2993,13 +2994,24 @@ void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId)
using namespace nxdn;
using namespace nxdn::defines;
if (m_nxdnN == 0U || m_nxdnN > 3U) {
::memset(m_nxdnAMBE, 0x00U, 36U);
::memcpy(m_nxdnAMBE + 0U, NULL_AMBE, RAW_AMBE_LENGTH_BYTES);
::memcpy(m_nxdnAMBE + 9U, NULL_AMBE, RAW_AMBE_LENGTH_BYTES);
::memcpy(m_nxdnAMBE + 18U, NULL_AMBE, RAW_AMBE_LENGTH_BYTES);
::memcpy(m_nxdnAMBE + 27U, NULL_AMBE, RAW_AMBE_LENGTH_BYTES);
if (m_nxdnN > 0U && m_nxdnN < 4U) {
// m_nxdnAMBE stores packed raw 49-bit payloads. Pad the
// incomplete frame with the raw payload represented by the
// standard NXDN null codeword.
nxdn::Audio nxdnAudio;
uint8_t nullPair[18U];
::memcpy(nullPair + 0U, NULL_AMBE, RAW_AMBE_LENGTH_BYTES);
::memcpy(nullPair + 9U, NULL_AMBE, RAW_AMBE_LENGTH_BYTES);
uint8_t nullBits[13U];
::memset(nullBits, 0x00U, sizeof(nullBits));
nxdnAudio.decode(nullPair, nullBits);
for (uint8_t n = m_nxdnN; n < 4U; n++) {
uint8_t* packedAMBE = m_nxdnAMBE + (n * RAW_AMBE_LENGTH_BYTES);
::memset(packedAMBE, 0x00U, RAW_AMBE_LENGTH_BYTES);
for (uint32_t bit = 0U; bit < 49U; bit++)
WRITE_BIT(packedAMBE, bit, READ_BIT(nullBits, bit));
}
::nxdn::lc::RTCH lc = ::nxdn::lc::RTCH();
lc.setMessageType(NXDDEF::MessageType::RTCH_VCALL);
@ -3043,7 +3055,20 @@ void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId)
sacch.setStructure(structures[superframeIndex]);
sacch.encode(frame);
::memcpy(frame + NXDN_FSW_LICH_SACCH_LENGTH_BYTES, m_nxdnAMBE, 36U);
uint8_t nxdnAudioPayload[36U];
::memset(nxdnAudioPayload, 0x00U, sizeof(nxdnAudioPayload));
for (uint8_t pair = 0U; pair < 2U; pair++) {
uint8_t packedBits[13U];
::memset(packedBits, 0x00U, sizeof(packedBits));
for (uint8_t half = 0U; half < 2U; half++) {
const uint8_t* ambe = m_nxdnAMBE + ((pair * 2U + half) * RAW_AMBE_LENGTH_BYTES);
for (uint32_t bit = 0U; bit < 49U; bit++)
WRITE_BIT(packedBits, (half * 49U) + bit, READ_BIT(ambe, bit));
}
nxdnAudio.encode(packedBits, nxdnAudioPayload + (pair * 18U));
}
::memcpy(frame + NXDN_FSW_LICH_SACCH_LENGTH_BYTES, nxdnAudioPayload, sizeof(nxdnAudioPayload));
NXDNUtils::scrambler(frame);
m_network->writeNXDN(lc, frame, NXDN_FRAME_LENGTH_BYTES);

@ -15,6 +15,8 @@
#include "common/edac/Golay24128.h"
#include "vocoder/MBEDecoder.h"
#include <cassert>
using namespace edac;
using namespace vocoder;
@ -152,6 +154,31 @@ int32_t MBEDecoder::decodeBits(uint8_t* codeword, char* mbeBits)
return errs;
}
/* Decodes a deinterleaved 49-bit AMBE payload to PCM samples. */
int32_t MBEDecoder::decodeBits(const uint8_t* mbeBits, int16_t samples[])
{
assert(mbeBits != nullptr);
assert(samples != nullptr);
if (m_mbeMode != DECODE_DMR_AMBE)
return -1;
char ambe[49U];
for (uint32_t i = 0U; i < 49U; i++)
ambe[i] = mbeBits[i] != 0U ? 1 : 0;
int errs = 0;
int errs2 = 0;
char errStr[64U];
::memset(errStr, 0x20U, sizeof(errStr));
mbe_processAmbe2450Data(samples, &errs, &errs2, errStr, ambe,
m_mbelibParms->m_cur_mp, m_mbelibParms->m_prev_mp,
m_mbelibParms->m_prev_mp_enhanced, 3);
return errs2;
}
/* Decodes the given MBE codewords to PCM samples using the decoder mode. */
int32_t MBEDecoder::decodeF(uint8_t* codeword, float samples[])

@ -95,6 +95,14 @@ namespace vocoder
*/
int32_t decodeBits(uint8_t* codeword, char* mbeBits);
/**
* @brief Decodes a deinterleaved 49-bit AMBE payload to PCM samples.
* @param[in] mbeBits Deinterleaved AMBE payload, one bit per byte.
* @param[out] samples PCM samples.
* @returns int32_t Decoder error count.
*/
int32_t decodeBits(const uint8_t* mbeBits, int16_t samples[]);
/**
* @brief Decodes the given MBE codewords to PCM samples using the decoder mode.
* @param[in] codeword MBE codeword.

@ -715,3 +715,23 @@ void MBEEncoder::encode(int16_t* samples, uint8_t* codeword)
::memcpy(codeword, dmrAMBE, 9U);
}
}
/* Encodes PCM samples to a deinterleaved 49-bit AMBE payload. */
void MBEEncoder::encodeBits(int16_t* samples, uint8_t bits[])
{
assert(samples != nullptr);
assert(bits != nullptr);
if (m_mbeMode != ENCODE_DMR_AMBE) {
::memset(bits, 0x00U, 49U);
return;
}
int16_t frameVector[8];
m_vocoder.imbe_encode(frameVector, samples);
int b[9];
encodeAMBE(m_vocoder.param(), b, &m_curMBEParms, &m_prevMBEParms, m_gainAdjust);
encode49bit(bits, b);
}

@ -66,6 +66,13 @@ namespace vocoder
*/
void encode(int16_t* samples, uint8_t* codeword);
/**
* @brief Encodes PCM samples to a deinterleaved 49-bit AMBE payload.
* @param[in] samples PCM samples.
* @param[out] bits Deinterleaved AMBE payload, one bit per byte.
*/
void encodeBits(int16_t* samples, uint8_t bits[]);
private:
imbe_vocoder m_vocoder;
mbe_parms m_curMBEParms;

Loading…
Cancel
Save

Powered by TurnKey Linux.