Basic jitter buffer for DMR, unfinished work.

This commit is contained in:
Jonathan Naylor 2017-11-23 11:54:45 +00:00
parent 1d33405a19
commit ce891019d3
7 changed files with 130 additions and 76 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -29,6 +29,7 @@ m_dstId(data.m_dstId),
m_flco(data.m_flco), m_flco(data.m_flco),
m_dataType(data.m_dataType), m_dataType(data.m_dataType),
m_seqNo(data.m_seqNo), m_seqNo(data.m_seqNo),
m_missing(data.m_missing),
m_n(data.m_n), m_n(data.m_n),
m_ber(data.m_ber), m_ber(data.m_ber),
m_rssi(data.m_rssi) m_rssi(data.m_rssi)
@ -45,6 +46,7 @@ m_dstId(0U),
m_flco(FLCO_GROUP), m_flco(FLCO_GROUP),
m_dataType(0U), m_dataType(0U),
m_seqNo(0U), m_seqNo(0U),
m_missing(false),
m_n(0U), m_n(0U),
m_ber(0U), m_ber(0U),
m_rssi(0U) m_rssi(0U)
@ -68,6 +70,7 @@ CDMRData& CDMRData::operator=(const CDMRData& data)
m_flco = data.m_flco; m_flco = data.m_flco;
m_dataType = data.m_dataType; m_dataType = data.m_dataType;
m_seqNo = data.m_seqNo; m_seqNo = data.m_seqNo;
m_missing = data.m_missing;
m_n = data.m_n; m_n = data.m_n;
m_ber = data.m_ber; m_ber = data.m_ber;
m_rssi = data.m_rssi; m_rssi = data.m_rssi;
@ -138,6 +141,16 @@ void CDMRData::setSeqNo(unsigned char seqNo)
m_seqNo = seqNo; m_seqNo = seqNo;
} }
bool CDMRData::isMissing() const
{
return m_missing;
}
void CDMRData::setMissing(bool missing)
{
m_missing = missing;
}
unsigned char CDMRData::getN() const unsigned char CDMRData::getN() const
{ {
return m_n; return m_n;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015,2016 by Jonathan Naylor, G4KLX * Copyright (C) 2015,2016,2017 by Jonathan Naylor, G4KLX
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -45,11 +45,14 @@ public:
unsigned char getDataType() const; unsigned char getDataType() const;
void setDataType(unsigned char dataType); void setDataType(unsigned char dataType);
bool isMissing() const;
void setMissing(bool missing);
unsigned char getBER() const; unsigned char getBER() const;
void setBER(unsigned char ber); void setBER(unsigned char ber);
unsigned char getRSSI() const; unsigned char getRSSI() const;
void setRSSI(unsigned char ber); void setRSSI(unsigned char rssi);
void setData(const unsigned char* buffer); void setData(const unsigned char* buffer);
unsigned int getData(unsigned char* buffer) const; unsigned int getData(unsigned char* buffer) const;
@ -62,6 +65,7 @@ private:
FLCO m_flco; FLCO m_flco;
unsigned char m_dataType; unsigned char m_dataType;
unsigned char m_seqNo; unsigned char m_seqNo;
bool m_missing;
unsigned char m_n; unsigned char m_n;
unsigned char m_ber; unsigned char m_ber;
unsigned char m_rssi; unsigned char m_rssi;

View file

@ -31,7 +31,7 @@ const unsigned int BUFFER_LENGTH = 500U;
const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U; 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_address(),
m_port(port), m_port(port),
m_id(NULL), m_id(NULL),
@ -43,6 +43,7 @@ m_socket(local),
m_enabled(false), m_enabled(false),
m_slot1(slot1), m_slot1(slot1),
m_slot2(slot2), m_slot2(slot2),
m_jitterBuffers(NULL),
m_hwType(hwType), m_hwType(hwType),
m_status(WAITING_CONNECT), m_status(WAITING_CONNECT),
m_retryTimer(1000U, 10U), m_retryTimer(1000U, 10U),
@ -69,17 +70,22 @@ m_beacon(false)
assert(port > 0U); assert(port > 0U);
assert(id > 1000U); assert(id > 1000U);
assert(!password.empty()); assert(!password.empty());
assert(jitter > 0U);
m_address = CUDPSocket::lookup(address); m_address = CUDPSocket::lookup(address);
m_buffer = new unsigned char[BUFFER_LENGTH]; m_buffer = new unsigned char[BUFFER_LENGTH];
m_salt = new unsigned char[sizeof(uint32_t)]; m_salt = new unsigned char[sizeof(uint32_t)];
m_id = new uint8_t[4U]; m_id = new uint8_t[4U];
m_streamId = new uint32_t[2U]; m_streamId = new uint32_t[2U];
m_jitterBuffers = new CJitterBuffer*[3U];
m_streamId[0U] = 0x00U; m_streamId[0U] = 0x00U;
m_streamId[1U] = 0x00U; m_streamId[1U] = 0x00U;
m_jitterBuffers[1U] = new CJitterBuffer(60U, DMR_SLOT_TIME, jitter, 256U);
m_jitterBuffers[2U] = new CJitterBuffer(60U, DMR_SLOT_TIME, jitter, 256U);
m_id[0U] = id >> 24; m_id[0U] = id >> 24;
m_id[1U] = id >> 16; m_id[1U] = id >> 16;
m_id[2U] = id >> 8; m_id[2U] = id >> 8;
@ -91,10 +97,14 @@ m_beacon(false)
CDMRNetwork::~CDMRNetwork() CDMRNetwork::~CDMRNetwork()
{ {
delete[] m_buffer; delete m_jitterBuffers[1U];
delete m_jitterBuffers[2U];
delete[] m_buffer;
delete[] m_salt; delete[] m_salt;
delete[] m_streamId; delete[] m_streamId;
delete[] m_id; delete[] m_id;
delete[] m_jitterBuffers;
} }
void CDMRNetwork::setOptions(const std::string& options) void CDMRNetwork::setOptions(const std::string& options)
@ -138,64 +148,77 @@ bool CDMRNetwork::read(CDMRData& data)
if (m_status != RUNNING) if (m_status != RUNNING)
return false; return false;
if (m_rxData.isEmpty()) if (!m_rxData.isEmpty()) {
return false; unsigned char length = 0U;
unsigned char length = 0U; m_rxData.getData(&length, 1U);
m_rxData.getData(m_buffer, length);
m_rxData.getData(&length, 1U); // Is this a data packet?
m_rxData.getData(m_buffer, length); if (::memcmp(m_buffer, "DMRD", 4U) == 0) {
unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U;
// Is this a data packet? bool wanted = true;
if (::memcmp(m_buffer, "DMRD", 4U) != 0)
return false;
unsigned char seqNo = m_buffer[4U]; // DMO mode slot disabling
if (slotNo == 1U && !m_duplex)
wanted = false;
unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); // Individual slot disabling
if (slotNo == 1U && !m_slot1)
wanted = false;
if (slotNo == 2U && !m_slot2)
wanted = false;
unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); if (wanted) {
unsigned char seqNo = m_buffer[4U];
unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; m_jitterBuffers[slotNo]->addData(m_buffer, seqNo);
}
// 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; for (unsigned int slotNo = 1U; slotNo <= 2U; slotNo++) {
JB_STATUS status = m_jitterBuffers[slotNo]->getData(m_buffer);
if (status != JBS_NO_DATA) {
unsigned char seqNo = m_buffer[4U];
unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0);
unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0);
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);
data.setMissing(status == JBS_MISSING);
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 false;
} }
bool CDMRNetwork::write(const CDMRData& data) bool CDMRNetwork::write(const CDMRData& data)
@ -339,6 +362,9 @@ void CDMRNetwork::close()
void CDMRNetwork::clock(unsigned int ms) void CDMRNetwork::clock(unsigned int ms)
{ {
m_jitterBuffers[1U]->clock(ms);
m_jitterBuffers[2U]->clock(ms);
if (m_status == WAITING_CONNECT) { if (m_status == WAITING_CONNECT) {
m_retryTimer.clock(ms); m_retryTimer.clock(ms);
if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) { if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) {
@ -480,6 +506,13 @@ void CDMRNetwork::clock(unsigned int ms)
} }
} }
void CDMRNetwork::reset(unsigned int slotNo)
{
assert(slotNo == 1U || slotNo == 2U);
m_jitterBuffers[slotNo]->reset();
}
bool CDMRNetwork::writeLogin() bool CDMRNetwork::writeLogin()
{ {
unsigned char buffer[8U]; unsigned char buffer[8U];

View file

@ -19,6 +19,7 @@
#if !defined(DMRNetwork_H) #if !defined(DMRNetwork_H)
#define DMRNetwork_H #define DMRNetwork_H
#include "JitterBuffer.h"
#include "UDPSocket.h" #include "UDPSocket.h"
#include "Timer.h" #include "Timer.h"
#include "RingBuffer.h" #include "RingBuffer.h"
@ -31,7 +32,7 @@
class CDMRNetwork class CDMRNetwork
{ {
public: 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(); ~CDMRNetwork();
void setOptions(const std::string& options); void setOptions(const std::string& options);
@ -54,21 +55,24 @@ public:
void clock(unsigned int ms); void clock(unsigned int ms);
void reset(unsigned int slotNo);
void close(); void close();
private: private:
in_addr m_address; in_addr m_address;
unsigned int m_port; unsigned int m_port;
uint8_t* m_id; uint8_t* m_id;
std::string m_password; std::string m_password;
bool m_duplex; bool m_duplex;
const char* m_version; const char* m_version;
bool m_debug; bool m_debug;
CUDPSocket m_socket; CUDPSocket m_socket;
bool m_enabled; bool m_enabled;
bool m_slot1; bool m_slot1;
bool m_slot2; bool m_slot2;
HW_TYPE m_hwType; CJitterBuffer** m_jitterBuffers;
HW_TYPE m_hwType;
enum STATUS { enum STATUS {
WAITING_CONNECT, WAITING_CONNECT,

View file

@ -131,8 +131,8 @@ JB_STATUS CJitterBuffer::getData(unsigned char* data)
::memset(data, 0x00U, m_blockSize); ::memset(data, 0x00U, m_blockSize);
m_headSequenceNumber++; m_headSequenceNumber++;
return JBS_REPEAT; return JBS_MISSING;
} }
void CJitterBuffer::reset() void CJitterBuffer::reset()

View file

@ -25,7 +25,7 @@
enum JB_STATUS { enum JB_STATUS {
JBS_NO_DATA, JBS_NO_DATA,
JBS_DATA, JBS_DATA,
JBS_REPEAT JBS_MISSING
}; };
class CJitterBuffer { class CJitterBuffer {

View file

@ -946,7 +946,7 @@ bool CMMDVMHost::createDMRNetwork()
LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled");
LogInfo(" Mode Hang: %us", m_dmrNetModeHang); 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(); std::string options = m_conf.getDMRNetworkOptions();
if (!options.empty()) { if (!options.empty()) {