From 526e2c436d3135e460d02da553c75170529d2a27 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 10 Jan 2018 19:05:59 +0000 Subject: [PATCH] Add the jitter buffer for DMR. --- DMRControl.cpp | 6 +- DMRControl.h | 4 +- DMRNetwork.cpp | 145 +++++++++++++++++++-------------- DMRNetwork.h | 37 +++++---- DMRSlot.cpp | 208 ++++++++++------------------------------------- DMRSlot.h | 16 +--- JitterBuffer.cpp | 33 ++++---- JitterBuffer.h | 7 +- MMDVM.ini | 2 +- MMDVMHost.cpp | 7 +- 10 files changed, 183 insertions(+), 282 deletions(-) diff --git a/DMRControl.cpp b/DMRControl.cpp index 4d7c668..88f58ed 100644 --- a/DMRControl.cpp +++ b/DMRControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX + * Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ #include #include -CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter) : +CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi) : m_colorCode(colorCode), m_modem(modem), m_network(network), @@ -38,7 +38,7 @@ m_lookup(lookup) // Load black and white lists to DMRAccessControl CDMRAccessControl::init(blacklist, whitelist, slot1TGWhitelist, slot2TGWhitelist, selfOnly, prefixes, id); - CDMRSlot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, display, duplex, m_lookup, rssi, jitter); + CDMRSlot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, display, duplex, m_lookup, rssi); } CDMRControl::~CDMRControl() diff --git a/DMRControl.h b/DMRControl.h index 7b7b17c..d034e6f 100644 --- a/DMRControl.h +++ b/DMRControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ class CDMRControl { public: - CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter); + CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi); ~CDMRControl(); bool processWakeup(const unsigned char* data); diff --git a/DMRNetwork.cpp b/DMRNetwork.cpp index 9aec33b..18ae163 100644 --- a/DMRNetwork.cpp +++ b/DMRNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,13 +25,14 @@ #include #include +#include const unsigned int BUFFER_LENGTH = 500U; const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U; -CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType) : +CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType, unsigned int jitter) : m_address(), m_port(port), m_id(NULL), @@ -43,6 +44,7 @@ m_socket(local), m_enabled(false), m_slot1(slot1), m_slot2(slot2), +m_jitterBuffers(NULL), m_hwType(hwType), m_status(WAITING_CONNECT), m_retryTimer(1000U, 10U), @@ -50,7 +52,6 @@ m_timeoutTimer(1000U, 60U), m_buffer(NULL), m_salt(NULL), m_streamId(NULL), -m_rxData(1000U, "DMR Network"), m_options(), m_callsign(), m_rxFrequency(0U), @@ -69,17 +70,22 @@ m_beacon(false) assert(port > 0U); assert(id > 1000U); assert(!password.empty()); + assert(jitter > 0U); m_address = CUDPSocket::lookup(address); - m_buffer = new unsigned char[BUFFER_LENGTH]; - m_salt = new unsigned char[sizeof(uint32_t)]; - m_id = new uint8_t[4U]; - m_streamId = new uint32_t[2U]; + m_buffer = new unsigned char[BUFFER_LENGTH]; + m_salt = new unsigned char[sizeof(uint32_t)]; + m_id = new uint8_t[4U]; + m_streamId = new uint32_t[2U]; + m_jitterBuffers = new CJitterBuffer*[3U]; m_streamId[0U] = 0x00U; m_streamId[1U] = 0x00U; + m_jitterBuffers[1U] = new CJitterBuffer("DMR Slot 1", 60U, DMR_SLOT_TIME, jitter, 256U, debug); + m_jitterBuffers[2U] = new CJitterBuffer("DMR Slot 2", 60U, DMR_SLOT_TIME, jitter, 256U, debug); + m_id[0U] = id >> 24; m_id[1U] = id >> 16; m_id[2U] = id >> 8; @@ -91,10 +97,14 @@ m_beacon(false) CDMRNetwork::~CDMRNetwork() { + delete m_jitterBuffers[1U]; + delete m_jitterBuffers[2U]; + delete[] m_buffer; delete[] m_salt; delete[] m_streamId; delete[] m_id; + delete[] m_jitterBuffers; } void CDMRNetwork::setOptions(const std::string& options) @@ -138,64 +148,49 @@ bool CDMRNetwork::read(CDMRData& data) if (m_status != RUNNING) return false; - if (m_rxData.isEmpty()) - return false; + for (unsigned int slotNo = 1U; slotNo <= 2U; slotNo++) { + unsigned int length = 0U; + JB_STATUS status = m_jitterBuffers[slotNo]->getData(m_buffer, length); + if (status != JBS_NO_DATA) { + unsigned char seqNo = m_buffer[4U]; - unsigned char length = 0U; + unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); - m_rxData.getData(&length, 1U); - m_rxData.getData(m_buffer, length); + unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); - // Is this a data packet? - if (::memcmp(m_buffer, "DMRD", 4U) != 0) - return false; + FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO_USER_USER : FLCO_GROUP; - unsigned char seqNo = m_buffer[4U]; + data.setSeqNo(seqNo); + data.setSlotNo(slotNo); + data.setSrcId(srcId); + data.setDstId(dstId); + data.setFLCO(flco); + data.setMissing(status == JBS_MISSING); - unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); + bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U; + bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U; - unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); + if (dataSync) { + unsigned char dataType = m_buffer[15U] & 0x0FU; + data.setData(m_buffer + 20U); + data.setDataType(dataType); + data.setN(0U); + } else if (voiceSync) { + data.setData(m_buffer + 20U); + data.setDataType(DT_VOICE_SYNC); + data.setN(0U); + } else { + unsigned char n = m_buffer[15U] & 0x0FU; + data.setData(m_buffer + 20U); + data.setDataType(DT_VOICE); + data.setN(n); + } - unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; - - // DMO mode slot disabling - if (slotNo == 1U && !m_duplex) - return false; - - // Individual slot disabling - if (slotNo == 1U && !m_slot1) - return false; - if (slotNo == 2U && !m_slot2) - return false; - - FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO_USER_USER : FLCO_GROUP; - - data.setSeqNo(seqNo); - data.setSlotNo(slotNo); - data.setSrcId(srcId); - data.setDstId(dstId); - data.setFLCO(flco); - - bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U; - bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U; - - if (dataSync) { - unsigned char dataType = m_buffer[15U] & 0x0FU; - data.setData(m_buffer + 20U); - data.setDataType(dataType); - data.setN(0U); - } else if (voiceSync) { - data.setData(m_buffer + 20U); - data.setDataType(DT_VOICE_SYNC); - data.setN(0U); - } else { - unsigned char n = m_buffer[15U] & 0x0FU; - data.setData(m_buffer + 20U); - data.setDataType(DT_VOICE); - data.setN(n); + return true; + } } - return true; + return false; } bool CDMRNetwork::write(const CDMRData& data) @@ -339,6 +334,9 @@ void CDMRNetwork::close() void CDMRNetwork::clock(unsigned int ms) { + m_jitterBuffers[1U]->clock(ms); + m_jitterBuffers[2U]->clock(ms); + if (m_status == WAITING_CONNECT) { m_retryTimer.clock(ms); if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) { @@ -376,10 +374,7 @@ void CDMRNetwork::clock(unsigned int ms) if (m_enabled) { if (m_debug) CUtils::dump(1U, "Network Received", m_buffer, length); - - unsigned char len = length; - m_rxData.addData(&len, 1U); - m_rxData.addData(m_buffer, len); + receiveData(m_buffer, length); } } else if (::memcmp(m_buffer, "MSTNAK", 6U) == 0) { if (m_status == RUNNING) { @@ -480,6 +475,34 @@ void CDMRNetwork::clock(unsigned int ms) } } +void CDMRNetwork::reset(unsigned int slotNo) +{ + assert(slotNo == 1U || slotNo == 2U); + + m_jitterBuffers[slotNo]->reset(); +} + +void CDMRNetwork::receiveData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned int slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U; + + // DMO mode slot disabling + if (slotNo == 1U && !m_duplex) + return; + + // Individual slot disabling + if (slotNo == 1U && !m_slot1) + return; + if (slotNo == 2U && !m_slot2) + return; + + unsigned char seqNo = data[4U]; + m_jitterBuffers[slotNo]->addData(data, length, seqNo); +} + bool CDMRNetwork::writeLogin() { unsigned char buffer[8U]; @@ -533,7 +556,7 @@ bool CDMRNetwork::writeConfig() slots = '1'; else if (!m_slot1 && m_slot2) slots = '2'; - + switch (m_hwType) { case HWT_MMDVM: software = "MMDVM"; diff --git a/DMRNetwork.h b/DMRNetwork.h index cc9cae4..53f579e 100644 --- a/DMRNetwork.h +++ b/DMRNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,9 +19,9 @@ #if !defined(DMRNetwork_H) #define DMRNetwork_H +#include "JitterBuffer.h" #include "UDPSocket.h" #include "Timer.h" -#include "RingBuffer.h" #include "DMRData.h" #include "Defines.h" @@ -31,7 +31,7 @@ class CDMRNetwork { public: - CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType); + CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType, unsigned int jitter); ~CDMRNetwork(); void setOptions(const std::string& options); @@ -54,21 +54,24 @@ public: void clock(unsigned int ms); + void reset(unsigned int slotNo); + void close(); private: - in_addr m_address; - unsigned int m_port; - uint8_t* m_id; - std::string m_password; - bool m_duplex; - const char* m_version; - bool m_debug; - CUDPSocket m_socket; - bool m_enabled; - bool m_slot1; - bool m_slot2; - HW_TYPE m_hwType; + in_addr m_address; + unsigned int m_port; + uint8_t* m_id; + std::string m_password; + bool m_duplex; + const char* m_version; + bool m_debug; + CUDPSocket m_socket; + bool m_enabled; + bool m_slot1; + bool m_slot2; + CJitterBuffer** m_jitterBuffers; + HW_TYPE m_hwType; enum STATUS { WAITING_CONNECT, @@ -86,8 +89,6 @@ private: unsigned char* m_salt; uint32_t* m_streamId; - CRingBuffer m_rxData; - std::string m_options; std::string m_callsign; @@ -111,6 +112,8 @@ private: bool writePing(); bool write(const unsigned char* data, unsigned int length); + + void receiveData(const unsigned char* data, unsigned int length); }; #endif diff --git a/DMRSlot.cpp b/DMRSlot.cpp index 7d37a4e..1d06ac3 100644 --- a/DMRSlot.cpp +++ b/DMRSlot.cpp @@ -45,9 +45,6 @@ unsigned int CDMRSlot::m_hangCount = 3U * 17U; CRSSIInterpolator* CDMRSlot::m_rssiMapper = NULL; -unsigned int CDMRSlot::m_jitterTime = 300U; -unsigned int CDMRSlot::m_jitterSlots = 5U; - unsigned char* CDMRSlot::m_idle = NULL; FLCO CDMRSlot::m_flco1; @@ -87,18 +84,16 @@ m_netTalkerId(TALKER_ID_NONE), m_rfLC(NULL), m_netLC(NULL), m_rfSeqNo(0U), -m_netSeqNo(0U), m_rfN(0U), m_netN(0U), m_networkWatchdog(1000U, 0U, 1500U), m_rfTimeoutTimer(1000U, timeout), m_netTimeoutTimer(1000U, timeout), -m_packetTimer(1000U, 0U, 50U), m_interval(), -m_elapsed(), m_rfFrames(0U), m_netFrames(0U), m_netLost(0U), +m_netMissed(0U), m_fec(), m_rfBits(1U), m_netBits(1U), @@ -106,8 +101,6 @@ m_rfErrs(0U), m_netErrs(0U), m_rfTimeout(false), m_netTimeout(false), -m_lastFrame(NULL), -m_lastFrameValid(false), m_rssi(0U), m_maxRSSI(0U), m_minRSSI(0U), @@ -116,7 +109,6 @@ m_rssiCount(0U), m_fp(NULL) { m_rfTalkerAlias = new unsigned char[32U]; - m_lastFrame = new unsigned char[DMR_FRAME_LENGTH_BYTES + 2U]; m_rfEmbeddedData = new CDMREmbeddedData[2U]; m_netEmbeddedData = new CDMREmbeddedData[2U]; @@ -128,7 +120,6 @@ CDMRSlot::~CDMRSlot() { delete[] m_rfEmbeddedData; delete[] m_netEmbeddedData; - delete[] m_lastFrame; delete[] m_rfTalkerAlias; } @@ -907,6 +898,9 @@ void CDMRSlot::writeEndRF(bool writeEnd) } } + if (m_network != NULL) + m_network->reset(m_slotNo); + m_rfTimeoutTimer.stop(); m_rfTimeout = false; @@ -926,8 +920,6 @@ void CDMRSlot::writeEndNet(bool writeEnd) m_display->clearDMR(m_slotNo); - m_lastFrameValid = false; - if (writeEnd && !m_netTimeout) { // Create a dummy start end frame unsigned char data[DMR_FRAME_LENGTH_BYTES + 2U]; @@ -954,9 +946,11 @@ void CDMRSlot::writeEndNet(bool writeEnd) } } + if (m_network != NULL) + m_network->reset(m_slotNo); + m_networkWatchdog.stop(); m_netTimeoutTimer.stop(); - m_packetTimer.stop(); m_netTimeout = false; m_netFrames = 0U; @@ -978,13 +972,22 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE) return; - m_networkWatchdog.start(); - - unsigned char dataType = dmrData.getDataType(); - unsigned char data[DMR_FRAME_LENGTH_BYTES + 2U]; dmrData.getData(data + 2U); + bool missing = dmrData.isMissing(); + + unsigned char dataType; + if (missing) { + m_netN = (m_netN + 1U) % 6U; + dataType = repeatFrame(data + 2U); + } else { + dataType = dmrData.getDataType(); + m_netN = dmrData.getN(); + m_networkWatchdog.start(); + m_netMissed = 0U; + } + if (dataType == DT_VOICE_LC_HEADER) { if (m_netState == RS_NET_AUDIO) return; @@ -1027,8 +1030,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) data[0U] = TAG_DATA; data[1U] = 0x00U; - m_lastFrameValid = false; - m_netTimeoutTimer.start(); m_netTimeout = false; @@ -1046,9 +1047,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) m_modem->writeDMRAbort(m_slotNo); } - for (unsigned int i = 0U; i < m_jitterSlots; i++) - writeQueueNet(m_idle); - if (m_duplex) { for (unsigned int i = 0U; i < NO_HEADERS_DUPLEX; i++) writeQueueNet(data); @@ -1080,8 +1078,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) m_netLC = lc; - m_lastFrameValid = false; - m_netTimeoutTimer.start(); m_netTimeout = false; @@ -1090,9 +1086,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) m_modem->writeDMRAbort(m_slotNo); } - for (unsigned int i = 0U; i < m_jitterSlots; i++) - writeQueueNet(m_idle); - // Create a dummy start frame unsigned char start[DMR_FRAME_LENGTH_BYTES + 2U]; @@ -1231,10 +1224,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) data[0U] = m_netFrames == 0U ? TAG_EOT : TAG_DATA; data[1U] = 0x00U; - // Put a small delay into starting transmission - writeQueueNet(m_idle); - writeQueueNet(m_idle); - writeQueueNet(data); m_netState = RS_NET_DATA; @@ -1266,8 +1255,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) m_netEmbeddedData[0U].setLC(*m_netLC); m_netEmbeddedData[1U].setLC(*m_netLC); - m_lastFrameValid = false; - m_netTimeoutTimer.start(); m_netTimeout = false; @@ -1276,9 +1263,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) m_modem->writeDMRAbort(m_slotNo); } - for (unsigned int i = 0U; i < m_jitterSlots; i++) - writeQueueNet(m_idle); - // Create a dummy start frame unsigned char start[DMR_FRAME_LENGTH_BYTES + 2U]; @@ -1339,17 +1323,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) // Convert the Audio Sync to be from the BS or MS as needed CSync::addDMRAudioSync(data + 2U, m_duplex); - // Initialise the lost packet data - if (m_netFrames == 0U) { - ::memcpy(m_lastFrame, data, DMR_FRAME_LENGTH_BYTES + 2U); - m_lastFrameValid = true; - m_netSeqNo = dmrData.getSeqNo(); - m_netN = dmrData.getN(); - m_netLost = 0U; - } else { - insertSilence(data, dmrData.getSeqNo()); - } - if (!m_netTimeout) writeQueueNet(data); @@ -1358,15 +1331,11 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) m_netEmbeddedData[m_netEmbeddedWriteN].reset(); - m_packetTimer.start(); - m_elapsed.start(); + if (missing) + m_netLost++; m_netFrames++; - // Save details in case we need to infill data - m_netSeqNo = dmrData.getSeqNo(); - m_netN = dmrData.getN(); - #if defined(DUMP_DMR) writeFile(data); #endif @@ -1493,29 +1462,14 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) data[0U] = TAG_DATA; data[1U] = 0x00U; - // Initialise the lost packet data - if (m_netFrames == 0U) { - ::memcpy(m_lastFrame, data, DMR_FRAME_LENGTH_BYTES + 2U); - m_lastFrameValid = true; - m_netSeqNo = dmrData.getSeqNo(); - m_netN = dmrData.getN(); - m_netLost = 0U; - } else { - insertSilence(data, dmrData.getSeqNo()); - } - if (!m_netTimeout) writeQueueNet(data); - m_packetTimer.start(); - m_elapsed.start(); + if (missing) + m_netLost++; m_netFrames++; - // Save details in case we need to infill data - m_netSeqNo = dmrData.getSeqNo(); - m_netN = dmrData.getN(); - #if defined(DUMP_DMR) writeFile(data); #endif @@ -1635,7 +1589,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) } } - void CDMRSlot::logGPSPosition(const unsigned char* data) { unsigned int errorI = (data[2U] & 0x0E) >> 1U; @@ -1725,21 +1678,6 @@ void CDMRSlot::clock() } } } - - if (m_netState == RS_NET_AUDIO) { - m_packetTimer.clock(ms); - - if (m_packetTimer.isRunning() && m_packetTimer.hasExpired()) { - unsigned int elapsed = m_elapsed.elapsed(); - if (elapsed >= m_jitterTime) { - LogDebug("DMR Slot %u, lost audio for %ums filling in", m_slotNo, elapsed); - insertSilence(m_jitterSlots); - m_elapsed.start(); - } - - m_packetTimer.start(); - } - } } void CDMRSlot::writeQueueRF(const unsigned char *data) @@ -1813,7 +1751,7 @@ void CDMRSlot::writeQueueNet(const unsigned char *data) m_queue.addData(data, len); } -void CDMRSlot::init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter) +void CDMRSlot::init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper) { assert(modem != NULL); assert(display != NULL); @@ -1832,9 +1770,6 @@ void CDMRSlot::init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData m_rssiMapper = rssiMapper; - m_jitterTime = jitter; - m_jitterSlots = jitter / DMR_SLOT_TIME; - m_idle = new unsigned char[DMR_FRAME_LENGTH_BYTES + 2U]; ::memcpy(m_idle, DMR_IDLE_DATA, DMR_FRAME_LENGTH_BYTES + 2U); @@ -1980,88 +1915,33 @@ void CDMRSlot::closeFile() } } -bool CDMRSlot::insertSilence(const unsigned char* data, unsigned char seqNo) +unsigned char CDMRSlot::repeatFrame(unsigned char* data) { - assert(data != NULL); - - // Check to see if we have any spaces to fill? - unsigned char seq = m_netSeqNo + 1U; - if (seq == seqNo) { - // Just copy the data, nothing else to do here - ::memcpy(m_lastFrame, data, DMR_FRAME_LENGTH_BYTES + 2U); - m_lastFrameValid = true; - return true; - } - - unsigned int oldSeqNo = m_netSeqNo + 1U; - unsigned int newSeqNo = seqNo; - - unsigned int count; - if (newSeqNo > oldSeqNo) - count = newSeqNo - oldSeqNo; - else - count = (256U + newSeqNo) - oldSeqNo; - - if (count >= 10U) - return false; - - insertSilence(count); - - ::memcpy(m_lastFrame, data, DMR_FRAME_LENGTH_BYTES + 2U); - m_lastFrameValid = true; - - return true; -} - -void CDMRSlot::insertSilence(unsigned int count) -{ - unsigned char data[DMR_FRAME_LENGTH_BYTES + 2U]; - - if (m_lastFrameValid) { - ::memcpy(data, m_lastFrame, 2U); // The control data - ::memcpy(data + 2U, m_lastFrame + 24U + 2U, 9U); // Copy the last audio block to the first - ::memcpy(data + 24U + 2U, data + 2U, 9U); // Copy the last audio block to the last - ::memcpy(data + 9U + 2U, data + 2U, 5U); // Copy the last audio block to the middle (1/2) - ::memcpy(data + 19U + 2U, data + 4U + 2U, 5U); // Copy the last audio block to the middle (2/2) + // Repeat the last audio for 60ms then silence after that + if (m_netMissed == 0U) { + ::memcpy(data, data + 24U, 9U); // Copy the last audio block to the first + ::memcpy(data + 9U, data + 24U, 5U); // Copy the last audio block to the middle (1/2) + ::memcpy(data + 19U, data + 24U, 5U); // Copy the last audio block to the middle (2/2) } else { - // Not sure what to do if this isn't AMBE audio - ::memcpy(data, DMR_SILENCE_DATA, DMR_FRAME_LENGTH_BYTES + 2U); + ::memcpy(data, DMR_SILENCE_DATA + 2U, DMR_FRAME_LENGTH_BYTES); } - unsigned char n = (m_netN + 1U) % 6U; - unsigned char seqNo = m_netSeqNo + 1U; + if (m_netN == 0U) { + CSync::addDMRAudioSync(data, m_duplex); - unsigned char fid = m_netLC->getFID(); + m_netMissed++; - CDMREMB emb; - emb.setColorCode(m_colorCode); - emb.setLCSS(0U); + return DT_VOICE_SYNC; + } else { + m_netEmbeddedLC.getData(data, 0U); - for (unsigned int i = 0U; i < count; i++) { - // Only use our silence frame if its AMBE audio data - if (fid == FID_ETSI || fid == FID_DMRA) { - if (i > 0U) { - ::memcpy(data, DMR_SILENCE_DATA, DMR_FRAME_LENGTH_BYTES + 2U); - m_lastFrameValid = false; - } - } + CDMREMB emb; + emb.setColorCode(m_colorCode); + emb.setLCSS(0U); + emb.getData(data); - if (n == 0U) { - CSync::addDMRAudioSync(data + 2U, m_duplex); - } else { - m_netEmbeddedLC.getData(data + 2U, 0U); - emb.getData(data + 2U); - } + m_netMissed++; - writeQueueNet(data); - - m_netSeqNo = seqNo; - m_netN = n; - - m_netFrames++; - m_netLost++; - - seqNo++; - n = (n + 1U) % 6U; + return DT_VOICE; } } diff --git a/DMRSlot.h b/DMRSlot.h index ffe1dde..daf835d 100644 --- a/DMRSlot.h +++ b/DMRSlot.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -57,7 +57,7 @@ public: void clock(); - static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter); + static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper); private: unsigned int m_slotNo; @@ -78,18 +78,16 @@ private: CDMRLC* m_rfLC; CDMRLC* m_netLC; unsigned char m_rfSeqNo; - unsigned char m_netSeqNo; unsigned char m_rfN; unsigned char m_netN; CTimer m_networkWatchdog; CTimer m_rfTimeoutTimer; CTimer m_netTimeoutTimer; - CTimer m_packetTimer; CStopWatch m_interval; - CStopWatch m_elapsed; unsigned int m_rfFrames; unsigned int m_netFrames; unsigned int m_netLost; + unsigned int m_netMissed; CAMBEFEC m_fec; unsigned int m_rfBits; unsigned int m_netBits; @@ -97,8 +95,6 @@ private: unsigned int m_netErrs; bool m_rfTimeout; bool m_netTimeout; - unsigned char* m_lastFrame; - bool m_lastFrameValid; unsigned char m_rssi; unsigned char m_maxRSSI; unsigned char m_minRSSI; @@ -120,9 +116,6 @@ private: static CRSSIInterpolator* m_rssiMapper; - static unsigned int m_jitterTime; - static unsigned int m_jitterSlots; - static unsigned char* m_idle; static FLCO m_flco1; @@ -146,8 +139,7 @@ private: bool writeFile(const unsigned char* data); void closeFile(); - bool insertSilence(const unsigned char* data, unsigned char seqNo); - void insertSilence(unsigned int count); + unsigned char repeatFrame(unsigned char* data); static void setShortLC(unsigned int slotNo, unsigned int id, FLCO flco = FLCO_GROUP, ACTIVITY_TYPE type = ACTIVITY_NONE); }; diff --git a/JitterBuffer.cpp b/JitterBuffer.cpp index 4a29f07..c77068f 100644 --- a/JitterBuffer.cpp +++ b/JitterBuffer.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,8 @@ #include #include -CJitterBuffer::CJitterBuffer(unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, unsigned int topSequenceNumber, bool debug) : +CJitterBuffer::CJitterBuffer(const std::string& name, unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, unsigned int topSequenceNumber, bool debug) : +m_name(name), m_blockSize(blockSize), m_blockTime(blockTime), m_topSequenceNumber(topSequenceNumber), @@ -43,7 +44,7 @@ m_lastDataLength(0U) assert(jitterTime > 0U); assert(topSequenceNumber > 0U); - m_blockCount = (jitterTime / blockTime) * 2U + 1U; + m_blockCount = topSequenceNumber / 2U; m_buffer = new JitterEntry[m_blockCount]; @@ -76,14 +77,12 @@ bool CJitterBuffer::addData(const unsigned char* data, unsigned int length, unsi // Is the data out of sequence? if (headSequenceNumber < tailSequenceNumber) { if (sequenceNumber < headSequenceNumber || sequenceNumber >= tailSequenceNumber) { - if (m_debug) - LogDebug("JitterBuffer: rejecting frame with seqNo=%u, raw=%u, head=%u, tail=%u", sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + LogDebug("%s, JitterBuffer: rejecting frame with seqNo=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); return false; } } else { if (sequenceNumber >= tailSequenceNumber && sequenceNumber < headSequenceNumber) { - if (m_debug) - LogDebug("JitterBuffer: rejecting frame with seqNo=%u, raw=%u, head=%u, tail=%u", sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + LogDebug("%s, JitterBuffer: rejecting frame with seqNo=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); return false; } } @@ -98,18 +97,18 @@ bool CJitterBuffer::addData(const unsigned char* data, unsigned int length, unsi // Do we already have the data? if (m_buffer[index].m_length > 0U) { - if (m_debug) - LogDebug("JitterBuffer: rejecting duplicate frame with seqNo=%u, raw=%u, head=%u, tail=%u", sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + LogDebug("%s, JitterBuffer: rejecting duplicate frame with seqNo=%u, pos=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), sequenceNumber, index, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); return false; } - LogDebug("JitterBuffer: adding frame with seqNo=%u, raw=%u, head=%u, tail=%u", sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); + if (m_debug) + LogDebug("%s, JitterBuffer: adding frame with seqNo=%u, pos=%u, raw=%u, head=%u, tail=%u", m_name.c_str(), sequenceNumber, index, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber); ::memcpy(m_buffer[index].m_data, data, length); m_buffer[index].m_length = length; if (!m_timer.isRunning()) { - LogDebug("JitterBuffer: starting the timer"); + LogDebug("%s, JitterBuffer: starting the timer", m_name.c_str()); m_timer.start(); } @@ -123,7 +122,7 @@ JB_STATUS CJitterBuffer::getData(unsigned char* data, unsigned int& length) if (!m_running) return JBS_NO_DATA; - unsigned int sequenceNumber = m_stopWatch.elapsed() / m_blockTime + 3U; + unsigned int sequenceNumber = m_stopWatch.elapsed() / m_blockTime + 2U; if (m_headSequenceNumber > sequenceNumber) return JBS_NO_DATA; @@ -132,7 +131,8 @@ JB_STATUS CJitterBuffer::getData(unsigned char* data, unsigned int& length) m_headSequenceNumber++; if (m_buffer[head].m_length > 0U) { - LogDebug("JitterBuffer: returning data, elapsed=%ums, raw=%u, head=%u", m_stopWatch.elapsed(), m_headSequenceNumber - 1U, head); + if (m_debug) + LogDebug("%s, JitterBuffer: returning data, elapsed=%ums, raw=%u, head=%u", m_name.c_str(), m_stopWatch.elapsed(), m_headSequenceNumber - 1U, head); ::memcpy(data, m_buffer[head].m_data, m_buffer[head].m_length); length = m_buffer[head].m_length; @@ -146,12 +146,13 @@ JB_STATUS CJitterBuffer::getData(unsigned char* data, unsigned int& length) return JBS_DATA; } - if (m_debug) - LogDebug("JitterBuffer: no data available, elapsed=%ums, raw=%u, head=%u", m_stopWatch.elapsed(), m_headSequenceNumber - 1U, head); + m_buffer[head].m_length = 0U; + + LogDebug("%s, JitterBuffer: no data available, elapsed=%ums, raw=%u, head=%u", m_name.c_str(), m_stopWatch.elapsed(), m_headSequenceNumber - 1U, head); // Return the last data frame if we have it if (m_lastDataLength > 0U) { - LogDebug("JitterBuffer: returning the last received frame"); + LogDebug("%s, JitterBuffer: returning the last received frame", m_name.c_str()); ::memcpy(data, m_lastData, m_lastDataLength); length = m_lastDataLength; diff --git a/JitterBuffer.h b/JitterBuffer.h index 37471e6..7bf664f 100644 --- a/JitterBuffer.h +++ b/JitterBuffer.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,8 @@ #include "StopWatch.h" #include "Timer.h" +#include + enum JB_STATUS { JBS_NO_DATA, JBS_DATA, @@ -30,7 +32,7 @@ enum JB_STATUS { class CJitterBuffer { public: - CJitterBuffer(unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, unsigned int topSequenceNumber, bool debug); + CJitterBuffer(const std::string& name, unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, unsigned int topSequenceNumber, bool debug); ~CJitterBuffer(); bool addData(const unsigned char* data, unsigned int length, unsigned int sequenceNumber); @@ -42,6 +44,7 @@ public: void clock(unsigned int ms); private: + std::string m_name; unsigned int m_blockSize; unsigned int m_blockTime; unsigned int m_topSequenceNumber; diff --git a/MMDVM.ini b/MMDVM.ini index ae371bd..16f9ec9 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -117,7 +117,7 @@ Debug=0 Enable=1 Address=44.131.4.1 Port=62031 -Jitter=300 +Jitter=500 # Local=62032 Password=PASSWORD # Options= diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index edd8800..61f3e73 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -380,7 +380,6 @@ int CMMDVMHost::run() std::vector slot2TGWhiteList = m_conf.getDMRSlot2TGWhiteList(); unsigned int callHang = m_conf.getDMRCallHang(); unsigned int txHang = m_conf.getDMRTXHang(); - unsigned int jitter = m_conf.getDMRNetworkJitter(); m_dmrRFModeHang = m_conf.getDMRModeHang(); if (txHang > m_dmrRFModeHang) @@ -415,7 +414,7 @@ int CMMDVMHost::run() LogInfo(" TX Hang: %us", txHang); LogInfo(" Mode Hang: %us", m_dmrRFModeHang); - dmr = new CDMRControl(id, colorCode, callHang, selfOnly, embeddedLCOnly, dumpTAData, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_lookup, rssi, jitter); + dmr = new CDMRControl(id, colorCode, callHang, selfOnly, embeddedLCOnly, dumpTAData, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_lookup, rssi); m_dmrTXTimer.setTimeout(txHang); } @@ -948,7 +947,7 @@ bool CMMDVMHost::createDMRNetwork() LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); LogInfo(" Mode Hang: %us", m_dmrNetModeHang); - m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, hwType); + m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, hwType, jitter); std::string options = m_conf.getDMRNetworkOptions(); if (!options.empty()) {