Update to the latest Kenwood protocol handler.

This commit is contained in:
Jonathan Naylor 2020-05-27 13:49:55 +01:00
parent 76bfc9c592
commit fc08cf95f3
5 changed files with 432 additions and 109 deletions

View File

@ -24,6 +24,7 @@
#include <cstdio> #include <cstdio>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <ctime>
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
@ -36,25 +37,37 @@ const unsigned int RTP_PORT = 64000U;
const unsigned int RTCP_PORT = 64001U; const unsigned int RTCP_PORT = 64001U;
CKenwoodNetwork::CKenwoodNetwork(const std::string& address, bool debug) : CKenwoodNetwork::CKenwoodNetwork(const std::string& address, bool debug) :
m_rtcpSocket(RTCP_PORT),
m_rtpSocket(RTP_PORT), m_rtpSocket(RTP_PORT),
m_stopWatch(), m_rtcpSocket(RTCP_PORT),
m_address(), m_address(),
m_headerSeen(false),
m_seen1(false),
m_seen2(false),
m_seen3(false),
m_seen4(false),
m_sacch(NULL),
m_sessionId(1U),
m_seqNo(0U), m_seqNo(0U),
m_timeStamp(0U),
m_ssrc(0U), m_ssrc(0U),
m_debug(debug), m_debug(debug),
m_timer(1000U, 0U, 200U) m_startSecs(0U),
m_startUSecs(0U),
m_rtcpTimer(1000U, 0U, 200U),
m_hangTimer(1000U, 5U),
m_hangType(0U),
m_hangSrc(0U),
m_hangDst(0U)
{ {
assert(!address.empty()); assert(!address.empty());
m_address = CUDPSocket::lookup(address); m_sacch = new unsigned char[10U];
::srand((unsigned int)m_stopWatch.time()); m_address = CUDPSocket::lookup(address);
} }
CKenwoodNetwork::~CKenwoodNetwork() CKenwoodNetwork::~CKenwoodNetwork()
{ {
delete[] m_sacch;
} }
bool CKenwoodNetwork::open() bool CKenwoodNetwork::open()
@ -72,7 +85,7 @@ bool CKenwoodNetwork::open()
return false; return false;
} }
m_ssrc = ::rand(); m_ssrc = m_rtpSocket.getLocalAddress();
return true; return true;
} }
@ -107,12 +120,12 @@ bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData)
outData[3U] = inData[3U]; outData[3U] = inData[3U];
// FACCH 1+2 // FACCH 1+2
outData[4U] = outData[14U] = inData[6U]; outData[4U] = outData[14U] = inData[6U];
outData[5U] = outData[15U] = inData[5U]; outData[5U] = outData[15U] = inData[5U];
outData[6U] = outData[16U] = inData[8U]; outData[6U] = outData[16U] = inData[8U];
outData[7U] = outData[17U] = inData[7U]; outData[7U] = outData[17U] = inData[7U];
outData[8U] = outData[18U] = inData[10U]; outData[8U] = outData[18U] = inData[10U];
outData[9U] = outData[19U] = inData[9U]; outData[9U] = outData[19U] = inData[9U];
outData[10U] = outData[20U] = inData[12U]; outData[10U] = outData[20U] = inData[12U];
outData[11U] = outData[21U] = inData[11U]; outData[11U] = outData[21U] = inData[11U];
@ -122,13 +135,16 @@ bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData)
switch (inData[5U] & 0x3FU) { switch (inData[5U] & 0x3FU) {
case 0x01U: case 0x01U:
m_timer.start(); m_hangTimer.stop();
writeRTCPData(type, src, dst); m_rtcpTimer.start();
writeRTCPStart();
return writeRTPVoiceHeader(outData); return writeRTPVoiceHeader(outData);
case 0x08U: case 0x08U: {
m_timer.stop(); m_hangTimer.start();
writeRTCPData(type, src, dst); bool ret = writeRTPVoiceTrailer(outData);
return writeRTPVoiceTrailer(outData); writeRTCPHang(type, src, dst);
return ret;
}
default: default:
return false; return false;
} }
@ -224,23 +240,27 @@ bool CKenwoodNetwork::writeRTPVoiceHeader(const unsigned char* data)
buffer[0U] = 0x80U; buffer[0U] = 0x80U;
buffer[1U] = 0x66U; buffer[1U] = 0x66U;
m_seqNo++;
buffer[2U] = (m_seqNo >> 8) & 0xFFU; buffer[2U] = (m_seqNo >> 8) & 0xFFU;
buffer[3U] = (m_seqNo >> 0) & 0xFFU; buffer[3U] = (m_seqNo >> 0) & 0xFFU;
m_seqNo++;
m_timeStamp = (unsigned long)m_stopWatch.time(); unsigned long timeStamp = getTimeStamp();
buffer[4U] = (timeStamp >> 24) & 0xFFU;
buffer[4U] = (m_timeStamp >> 24) & 0xFFU; buffer[5U] = (timeStamp >> 16) & 0xFFU;
buffer[5U] = (m_timeStamp >> 16) & 0xFFU; buffer[6U] = (timeStamp >> 8) & 0xFFU;
buffer[6U] = (m_timeStamp >> 8) & 0xFFU; buffer[7U] = (timeStamp >> 0) & 0xFFU;
buffer[7U] = (m_timeStamp >> 0) & 0xFFU;
m_timeStamp += 640U;
buffer[8U] = (m_ssrc >> 24) & 0xFFU; buffer[8U] = (m_ssrc >> 24) & 0xFFU;
buffer[9U] = (m_ssrc >> 16) & 0xFFU; buffer[9U] = (m_ssrc >> 16) & 0xFFU;
buffer[10U] = (m_ssrc >> 8) & 0xFFU; buffer[10U] = (m_ssrc >> 8) & 0xFFU;
buffer[11U] = (m_ssrc >> 0) & 0xFFU; buffer[11U] = (m_ssrc >> 0) & 0xFFU;
m_sessionId++;
buffer[12U] = m_sessionId;
buffer[13U] = 0x00U;
buffer[14U] = 0x00U;
buffer[15U] = 0x00U;
buffer[16U] = 0x03U; buffer[16U] = 0x03U;
buffer[17U] = 0x03U; buffer[17U] = 0x03U;
buffer[18U] = 0x04U; buffer[18U] = 0x04U;
@ -267,19 +287,26 @@ bool CKenwoodNetwork::writeRTPVoiceTrailer(const unsigned char* data)
buffer[0U] = 0x80U; buffer[0U] = 0x80U;
buffer[1U] = 0x66U; buffer[1U] = 0x66U;
m_seqNo++;
buffer[2U] = (m_seqNo >> 8) & 0xFFU; buffer[2U] = (m_seqNo >> 8) & 0xFFU;
buffer[3U] = (m_seqNo >> 0) & 0xFFU; buffer[3U] = (m_seqNo >> 0) & 0xFFU;
buffer[4U] = (m_timeStamp >> 24) & 0xFFU; unsigned long timeStamp = getTimeStamp();
buffer[5U] = (m_timeStamp >> 16) & 0xFFU; buffer[4U] = (timeStamp >> 24) & 0xFFU;
buffer[6U] = (m_timeStamp >> 8) & 0xFFU; buffer[5U] = (timeStamp >> 16) & 0xFFU;
buffer[7U] = (m_timeStamp >> 0) & 0xFFU; buffer[6U] = (timeStamp >> 8) & 0xFFU;
buffer[7U] = (timeStamp >> 0) & 0xFFU;
buffer[8U] = (m_ssrc >> 24) & 0xFFU; buffer[8U] = (m_ssrc >> 24) & 0xFFU;
buffer[9U] = (m_ssrc >> 16) & 0xFFU; buffer[9U] = (m_ssrc >> 16) & 0xFFU;
buffer[10U] = (m_ssrc >> 8) & 0xFFU; buffer[10U] = (m_ssrc >> 8) & 0xFFU;
buffer[11U] = (m_ssrc >> 0) & 0xFFU; buffer[11U] = (m_ssrc >> 0) & 0xFFU;
buffer[12U] = m_sessionId;
buffer[13U] = 0x00U;
buffer[14U] = 0x00U;
buffer[15U] = 0x00U;
buffer[16U] = 0x03U; buffer[16U] = 0x03U;
buffer[17U] = 0x03U; buffer[17U] = 0x03U;
buffer[18U] = 0x04U; buffer[18U] = 0x04U;
@ -306,21 +333,26 @@ bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data)
buffer[0U] = 0x80U; buffer[0U] = 0x80U;
buffer[1U] = 0x66U; buffer[1U] = 0x66U;
m_seqNo++;
buffer[2U] = (m_seqNo >> 8) & 0xFFU; buffer[2U] = (m_seqNo >> 8) & 0xFFU;
buffer[3U] = (m_seqNo >> 0) & 0xFFU; buffer[3U] = (m_seqNo >> 0) & 0xFFU;
m_seqNo++;
buffer[4U] = (m_timeStamp >> 24) & 0xFFU; unsigned long timeStamp = getTimeStamp();
buffer[5U] = (m_timeStamp >> 16) & 0xFFU; buffer[4U] = (timeStamp >> 24) & 0xFFU;
buffer[6U] = (m_timeStamp >> 8) & 0xFFU; buffer[5U] = (timeStamp >> 16) & 0xFFU;
buffer[7U] = (m_timeStamp >> 0) & 0xFFU; buffer[6U] = (timeStamp >> 8) & 0xFFU;
m_timeStamp += 640U; buffer[7U] = (timeStamp >> 0) & 0xFFU;
buffer[8U] = (m_ssrc >> 24) & 0xFFU; buffer[8U] = (m_ssrc >> 24) & 0xFFU;
buffer[9U] = (m_ssrc >> 16) & 0xFFU; buffer[9U] = (m_ssrc >> 16) & 0xFFU;
buffer[10U] = (m_ssrc >> 8) & 0xFFU; buffer[10U] = (m_ssrc >> 8) & 0xFFU;
buffer[11U] = (m_ssrc >> 0) & 0xFFU; buffer[11U] = (m_ssrc >> 0) & 0xFFU;
buffer[12U] = m_sessionId;
buffer[13U] = 0x00U;
buffer[14U] = 0x00U;
buffer[15U] = 0x00U;
buffer[16U] = 0x03U; buffer[16U] = 0x03U;
buffer[17U] = 0x02U; buffer[17U] = 0x02U;
buffer[18U] = 0x04U; buffer[18U] = 0x04U;
@ -337,30 +369,59 @@ bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data)
return m_rtpSocket.write(buffer, 59U, m_address, RTP_PORT); return m_rtpSocket.write(buffer, 59U, m_address, RTP_PORT);
} }
bool CKenwoodNetwork::writeRTCPPing() bool CKenwoodNetwork::writeRTCPStart()
{ {
#if defined(_WIN32) || defined(_WIN64)
time_t now;
::time(&now);
m_startSecs = uint32_t(now);
SYSTEMTIME st;
::GetSystemTime(&st);
m_startUSecs = st.wMilliseconds * 1000U;
#else
struct timeval tod;
::gettimeofday(&tod, NULL);
m_startSecs = tod.tv_sec;
m_startUSecs = tod.tv_usec;
#endif
unsigned char buffer[30U]; unsigned char buffer[30U];
::memset(buffer, 0x00U, 30U); ::memset(buffer, 0x00U, 30U);
buffer[0U] = 0x8AU; buffer[0U] = 0x8AU;
buffer[1U] = 0xCCU; buffer[1U] = 0xCCU;
buffer[2U] = 0x00U;
buffer[3U] = 0x06U; buffer[3U] = 0x06U;
buffer[4U] = (m_ssrc >> 24) & 0xFFU; buffer[4U] = (m_ssrc >> 24) & 0xFFU;
buffer[5U] = (m_ssrc >> 16) & 0xFFU; buffer[5U] = (m_ssrc >> 16) & 0xFFU;
buffer[6U] = (m_ssrc >> 8) & 0xFFU; buffer[6U] = (m_ssrc >> 8) & 0xFFU;
buffer[7U] = (m_ssrc >> 0) & 0xFFU; buffer[7U] = (m_ssrc >> 0) & 0xFFU;
buffer[8U] = 'K'; buffer[8U] = 'K';
buffer[9U] = 'W'; buffer[9U] = 'W';
buffer[10U] = 'N'; buffer[10U] = 'N';
buffer[11U] = 'E'; buffer[11U] = 'E';
buffer[12U] = (m_startSecs >> 24) & 0xFFU;
buffer[13U] = (m_startSecs >> 16) & 0xFFU;
buffer[14U] = (m_startSecs >> 8) & 0xFFU;
buffer[15U] = (m_startSecs >> 0) & 0xFFU;
buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
buffer[22U] = 0x02U; buffer[22U] = 0x02U;
buffer[24U] = 0x01U; buffer[24U] = 0x01U;
buffer[25U] = 0x01U;
buffer[27U] = 0x0AU;
if (m_debug) if (m_debug)
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U); CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
@ -368,33 +429,84 @@ bool CKenwoodNetwork::writeRTCPPing()
return m_rtcpSocket.write(buffer, 28U, m_address, RTCP_PORT); return m_rtcpSocket.write(buffer, 28U, m_address, RTCP_PORT);
} }
bool CKenwoodNetwork::writeRTCPData(unsigned char type, unsigned short src, unsigned short dst) bool CKenwoodNetwork::writeRTCPPing()
{ {
unsigned char buffer[20U]; unsigned char buffer[30U];
::memset(buffer, 0x00U, 20U); ::memset(buffer, 0x00U, 30U);
buffer[0U] = 0x8BU; buffer[0U] = 0x8AU;
buffer[1U] = 0xCCU; buffer[1U] = 0xCCU;
buffer[2U] = 0x00U;
buffer[3U] = 0x04U; buffer[3U] = 0x06U;
buffer[4U] = (m_ssrc >> 24) & 0xFFU; buffer[4U] = (m_ssrc >> 24) & 0xFFU;
buffer[5U] = (m_ssrc >> 16) & 0xFFU; buffer[5U] = (m_ssrc >> 16) & 0xFFU;
buffer[6U] = (m_ssrc >> 8) & 0xFFU; buffer[6U] = (m_ssrc >> 8) & 0xFFU;
buffer[7U] = (m_ssrc >> 0) & 0xFFU; buffer[7U] = (m_ssrc >> 0) & 0xFFU;
buffer[8U] = 'K'; buffer[8U] = 'K';
buffer[9U] = 'W'; buffer[9U] = 'W';
buffer[10U] = 'N'; buffer[10U] = 'N';
buffer[11U] = 'E'; buffer[11U] = 'E';
buffer[12U] = (src >> 8) & 0xFFU; buffer[12U] = (m_startSecs >> 24) & 0xFFU;
buffer[13U] = (src >> 0) & 0xFFU; buffer[13U] = (m_startSecs >> 16) & 0xFFU;
buffer[14U] = (m_startSecs >> 8) & 0xFFU;
buffer[15U] = (m_startSecs >> 0) & 0xFFU;
buffer[14U] = (dst >> 8) & 0xFFU; buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
buffer[15U] = (dst >> 0) & 0xFFU; buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
buffer[16U] = type; buffer[22U] = 0x02U;
buffer[24U] = 0x01U;
buffer[27U] = 0x7BU;
if (m_debug)
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
return m_rtcpSocket.write(buffer, 28U, m_address, RTCP_PORT);
}
bool CKenwoodNetwork::writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst)
{
m_hangType = type;
m_hangSrc = src;
m_hangDst = dst;
return writeRTCPHang();
}
bool CKenwoodNetwork::writeRTCPHang()
{
unsigned char buffer[30U];
::memset(buffer, 0x00U, 30U);
buffer[0U] = 0x8BU;
buffer[1U] = 0xCCU;
buffer[2U] = 0x00U;
buffer[3U] = 0x04U;
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
buffer[8U] = 'K';
buffer[9U] = 'W';
buffer[10U] = 'N';
buffer[11U] = 'E';
buffer[12U] = (m_hangSrc >> 8) & 0xFFU;
buffer[13U] = (m_hangSrc >> 0) & 0xFFU;
buffer[14U] = (m_hangDst >> 8) & 0xFFU;
buffer[15U] = (m_hangDst >> 0) & 0xFFU;
buffer[16U] = m_hangType;
if (m_debug) if (m_debug)
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U); CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U);
@ -409,26 +521,23 @@ unsigned int CKenwoodNetwork::read(unsigned char* data)
unsigned char dummy[BUFFER_LENGTH]; unsigned char dummy[BUFFER_LENGTH];
readRTCP(dummy); readRTCP(dummy);
bool ret;
unsigned int len = readRTP(data); unsigned int len = readRTP(data);
if (len > 0U) { switch (len) {
switch (data[9U]) { case 0U: // Nothing received
case 0x05U: // Voice header or trailer return 0U;
ret = processKenwoodVoiceHeader(data); case 35U: // Voice header or trailer
if (!ret) return processKenwoodVoiceHeader(data);
return 0U; case 47U: // Voice data
return 33U; if (m_headerSeen)
case 0x08U: // Voice data return processKenwoodVoiceData(data);
processKenwoodVoiceData(data); else
return 33U; return processKenwoodVoiceLateEntry(data);
default: case 31U: // Data
break; return processKenwoodData(data);
} default:
CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len);
return 0U;
} }
CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len);
return 0U;
} }
unsigned int CKenwoodNetwork::readRTP(unsigned char* data) unsigned int CKenwoodNetwork::readRTP(unsigned char* data)
@ -444,19 +553,14 @@ unsigned int CKenwoodNetwork::readRTP(unsigned char* data)
return 0U; return 0U;
// Check if the data is for us // Check if the data is for us
if (m_address.s_addr != address.s_addr || port != RTP_PORT) { if (m_address.s_addr != address.s_addr) {
LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, RTP_PORT, port); LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
return 0U; return 0U;
} }
if (m_debug) if (m_debug)
CUtils::dump(1U, "Kenwood Network RTP Data Received", buffer, length); CUtils::dump(1U, "Kenwood Network RTP Data Received", buffer, length);
if (length != 47 && length != 59) {
LogError("Invalid RTP length of %d", length);
return 0U;
}
::memcpy(data, buffer + 12U, length - 12U); ::memcpy(data, buffer + 12U, length - 12U);
return length - 12U; return length - 12U;
@ -475,19 +579,14 @@ unsigned int CKenwoodNetwork::readRTCP(unsigned char* data)
return 0U; return 0U;
// Check if the data is for us // Check if the data is for us
if (m_address.s_addr != address.s_addr || port != RTCP_PORT) { if (m_address.s_addr != address.s_addr) {
LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, RTCP_PORT, port); LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
return 0U; return 0U;
} }
if (m_debug) if (m_debug)
CUtils::dump(1U, "Kenwood Network RTCP Data Received", buffer, length); CUtils::dump(1U, "Kenwood Network RTCP Data Received", buffer, length);
if (length != 20 && length != 28) {
LogError("Invalid RTCP length of %d", length);
return 0U;
}
if (::memcmp(buffer + 8U, "KWNE", 4U) != 0) { if (::memcmp(buffer + 8U, "KWNE", 4U) != 0) {
LogError("Missing RTCP KWNE signature"); LogError("Missing RTCP KWNE signature");
return 0U; return 0U;
@ -508,14 +607,23 @@ void CKenwoodNetwork::close()
void CKenwoodNetwork::clock(unsigned int ms) void CKenwoodNetwork::clock(unsigned int ms)
{ {
m_timer.clock(ms); m_rtcpTimer.clock(ms);
if (m_timer.isRunning() && m_timer.hasExpired()) { if (m_rtcpTimer.isRunning() && m_rtcpTimer.hasExpired()) {
writeRTCPPing(); if (m_hangTimer.isRunning())
m_timer.start(); writeRTCPHang();
else
writeRTCPPing();
m_rtcpTimer.start();
}
m_hangTimer.clock(ms);
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
m_rtcpTimer.stop();
m_hangTimer.stop();
} }
} }
bool CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData) unsigned int CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
{ {
assert(inData != NULL); assert(inData != NULL);
@ -552,15 +660,27 @@ bool CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
switch (outData[5U] & 0x3FU) { switch (outData[5U] & 0x3FU) {
case 0x01U: case 0x01U:
::memcpy(inData, outData, 33U);
m_headerSeen = true;
m_seen1 = false;
m_seen2 = false;
m_seen3 = false;
m_seen4 = false;
return 33U;
case 0x08U: case 0x08U:
::memcpy(inData, outData, 33U); ::memcpy(inData, outData, 33U);
return true; m_headerSeen = false;
m_seen1 = false;
m_seen2 = false;
m_seen3 = false;
m_seen4 = false;
return 33U;
default: default:
return false; return 0U;
} }
} }
void CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData) unsigned int CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
{ {
assert(inData != NULL); assert(inData != NULL);
@ -642,4 +762,162 @@ void CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
} }
::memcpy(inData, outData, 33U); ::memcpy(inData, outData, 33U);
return 33U;
}
unsigned int CKenwoodNetwork::processKenwoodData(unsigned char* inData)
{
if (inData[7U] != 0x09U && inData[7U] != 0x0BU && inData[7U] != 0x08U)
return 0U;
unsigned char outData[50U];
if (inData[7U] == 0x09U || inData[7U] == 0x08U) {
outData[0U] = 0x90U;
outData[1U] = inData[8U];
outData[2U] = inData[7U];
outData[3U] = inData[10U];
outData[4U] = inData[9U];
outData[5U] = inData[12U];
outData[6U] = inData[11U];
::memcpy(inData, outData, 7U);
return 7U;
} else {
outData[0U] = 0x90U;
outData[1U] = inData[8U];
outData[2U] = inData[7U];
outData[3U] = inData[10U];
outData[4U] = inData[9U];
outData[5U] = inData[12U];
outData[6U] = inData[11U];
outData[7U] = inData[14U];
outData[8U] = inData[13U];
outData[9U] = inData[16U];
outData[10U] = inData[15U];
outData[11U] = inData[18U];
outData[12U] = inData[17U];
outData[13U] = inData[20U];
outData[14U] = inData[19U];
outData[15U] = inData[22U];
outData[16U] = inData[21U];
outData[17U] = inData[24U];
outData[18U] = inData[23U];
outData[19U] = inData[26U];
outData[20U] = inData[25U];
outData[21U] = inData[28U];
outData[22U] = inData[27U];
outData[23U] = inData[29U];
::memcpy(inData, outData, 24U);
return 24U;
}
}
unsigned long CKenwoodNetwork::getTimeStamp() const
{
unsigned long timeStamp = 0UL;
#if defined(_WIN32) || defined(_WIN64)
SYSTEMTIME st;
::GetSystemTime(&st);
unsigned int hh = st.wHour;
unsigned int mm = st.wMinute;
unsigned int ss = st.wSecond;
unsigned int ms = st.wMilliseconds;
timeStamp += hh * 3600U * 1000U * 80U;
timeStamp += mm * 60U * 1000U * 80U;
timeStamp += ss * 1000U * 80U;
timeStamp += ms * 80U;
#else
struct timeval tod;
::gettimeofday(&tod, NULL);
unsigned int ss = tod.tv_sec;
unsigned int ms = tod.tv_usec / 1000U;
timeStamp += ss * 1000U * 80U;
timeStamp += ms * 80U;
#endif
return timeStamp;
}
unsigned int CKenwoodNetwork::processKenwoodVoiceLateEntry(unsigned char* inData)
{
assert(inData != NULL);
unsigned char sacch[4U];
sacch[0U] = inData[12U];
sacch[1U] = inData[11U];
sacch[2U] = inData[14U];
sacch[3U] = inData[13U];
switch (sacch[0U] & 0xC0U) {
case 0xC0U:
if (!m_seen1) {
unsigned int offset = 0U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen1 = true;
}
break;
case 0x80U:
if (!m_seen2) {
unsigned int offset = 18U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen2 = true;
}
break;
case 0x40U:
if (!m_seen3) {
unsigned int offset = 36U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen3 = true;
}
break;
case 0x00U:
if (!m_seen4) {
unsigned int offset = 54U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen4 = true;
}
break;
}
if (!m_seen1 || !m_seen2 || !m_seen3 || !m_seen4)
return 0U;
// Create a dummy header
// Header SACCH
inData[11U] = 0x10U;
inData[12U] = 0x01U;
inData[13U] = 0x00U;
inData[14U] = 0x00U;
// Header FACCH
inData[15U] = m_sacch[1U];
inData[16U] = m_sacch[0U];
inData[17U] = m_sacch[3U];
inData[18U] = m_sacch[2U];
inData[19U] = m_sacch[5U];
inData[20U] = m_sacch[4U];
inData[21U] = m_sacch[7U];
inData[22U] = m_sacch[6U];
inData[23U] = 0x00U;
inData[24U] = m_sacch[8U];
return processKenwoodVoiceHeader(inData);
} }

View File

@ -20,7 +20,6 @@
#define KenwoodNetwork_H #define KenwoodNetwork_H
#include "CoreNetwork.h" #include "CoreNetwork.h"
#include "StopWatch.h"
#include "UDPSocket.h" #include "UDPSocket.h"
#include "Timer.h" #include "Timer.h"
@ -45,25 +44,41 @@ public:
private: private:
CUDPSocket m_rtpSocket; CUDPSocket m_rtpSocket;
CUDPSocket m_rtcpSocket; CUDPSocket m_rtcpSocket;
CStopWatch m_stopWatch;
in_addr m_address; in_addr m_address;
unsigned short m_seqNo; bool m_headerSeen;
unsigned long m_timeStamp; bool m_seen1;
bool m_seen2;
bool m_seen3;
bool m_seen4;
unsigned char* m_sacch;
uint8_t m_sessionId;
uint16_t m_seqNo;
unsigned int m_ssrc; unsigned int m_ssrc;
bool m_debug; bool m_debug;
CTimer m_timer; uint32_t m_startSecs;
uint32_t m_startUSecs;
CTimer m_rtcpTimer;
CTimer m_hangTimer;
unsigned char m_hangType;
unsigned short m_hangSrc;
unsigned short m_hangDst;
bool processIcomVoiceHeader(const unsigned char* data); bool processIcomVoiceHeader(const unsigned char* data);
bool processIcomVoiceData(const unsigned char* data); bool processIcomVoiceData(const unsigned char* data);
bool processKenwoodVoiceHeader(unsigned char* data); unsigned int processKenwoodVoiceHeader(unsigned char* data);
void processKenwoodVoiceData(unsigned char* data); unsigned int processKenwoodVoiceData(unsigned char* data);
unsigned int processKenwoodVoiceLateEntry(unsigned char* data);
unsigned int processKenwoodData(unsigned char* data);
bool writeRTPVoiceHeader(const unsigned char* data); bool writeRTPVoiceHeader(const unsigned char* data);
bool writeRTPVoiceData(const unsigned char* data); bool writeRTPVoiceData(const unsigned char* data);
bool writeRTPVoiceTrailer(const unsigned char* data); bool writeRTPVoiceTrailer(const unsigned char* data);
bool writeRTCPStart();
bool writeRTCPPing(); bool writeRTCPPing();
bool writeRTCPData(unsigned char type, unsigned short src, unsigned short dst); bool writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst);
bool writeRTCPHang();
unsigned int readRTP(unsigned char* data); unsigned int readRTP(unsigned char* data);
unsigned int readRTCP(unsigned char* data); unsigned int readRTCP(unsigned char* data);
unsigned long getTimeStamp() const;
}; };
#endif #endif

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2006-2016 by Jonathan Naylor G4KLX * Copyright (C) 2006-2016,2020 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
@ -260,3 +260,31 @@ void CUDPSocket::close()
::close(m_fd); ::close(m_fd);
#endif #endif
} }
unsigned long CUDPSocket::getLocalAddress() const
{
unsigned long address = 0UL;
char hostname[80U];
int ret = ::gethostname(hostname, 80);
if (ret == -1)
return 0UL;
struct hostent* phe = ::gethostbyname(hostname);
if (phe == NULL)
return 0UL;
if (phe->h_addrtype != AF_INET)
return 0UL;
for (unsigned int i = 0U; phe->h_addr_list[i] != NULL; i++) {
struct in_addr addr;
::memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
if (addr.s_addr != INADDR_LOOPBACK) {
address = addr.s_addr;
break;
}
}
return address;
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2009-2011,2013,2015,2016 by Jonathan Naylor G4KLX * Copyright (C) 2009-2011,2013,2015,2016,2020 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
@ -47,7 +47,9 @@ public:
void close(); void close();
static in_addr lookup(const std::string& hostName); unsigned long getLocalAddress() const;
static in_addr lookup(const std::string& hostName);
private: private:
std::string m_address; std::string m_address;

View File

@ -19,6 +19,6 @@
#if !defined(VERSION_H) #if !defined(VERSION_H)
#define VERSION_H #define VERSION_H
const char* VERSION = "20200420"; const char* VERSION = "20200427";
#endif #endif