Remove the NXDN Reflector.

This commit is contained in:
Jonathan Naylor 2023-02-23 19:34:37 +00:00
parent 3bc2a1415b
commit 486106a9c7
39 changed files with 3 additions and 10446 deletions

View File

@ -1,4 +1,4 @@
SUBDIRS = NXDNGateway NXDNParrot NXDNReflector
SUBDIRS = NXDNGateway NXDNParrot
CLEANDIRS = $(SUBDIRS:%=clean-%)
INSTALLDIRS = $(SUBDIRS:%=install-%)

View File

@ -1,14 +1,12 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2010
# Visual Studio Version 16
VisualStudioVersion = 16.0.31105.61
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNParrot", "NXDNParrot\NXDNParrot.vcxproj", "{2AE94EAA-FD57-45C9-8555-6425CFA777A3}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNGateway", "NXDNGateway\NXDNGateway.vcxproj", "{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNReflector", "NXDNReflector\NXDNReflector.vcxproj", "{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -33,14 +31,6 @@ Global
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x64.Build.0 = Release|x64
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x86.ActiveCfg = Release|Win32
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x86.Build.0 = Release|Win32
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.ActiveCfg = Debug|x64
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.Build.0 = Debug|x64
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.ActiveCfg = Debug|Win32
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.Build.0 = Debug|Win32
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.ActiveCfg = Release|x64
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.Build.0 = Release|x64
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.ActiveCfg = Release|Win32
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -1,287 +0,0 @@
/*
* Copyright (C) 2015,2016,2018,2020 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Conf.h"
#include "Log.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
const int BUFFER_SIZE = 500;
enum SECTION {
SECTION_NONE,
SECTION_GENERAL,
SECTION_ID_LOOKUP,
SECTION_LOG,
SECTION_NETWORK,
SECTION_ICOM_NETWORK,
SECTION_KENWOOD_NETWORK
};
CConf::CConf(const std::string& file) :
m_file(file),
m_tg(9999U),
m_daemon(false),
m_lookupName(),
m_lookupTime(0U),
m_logDisplayLevel(0U),
m_logFileLevel(0U),
m_logFilePath(),
m_logFileRoot(),
m_logFileRotate(true),
m_networkPort(0U),
m_networkDebug(false),
m_icomEnabled(false),
m_icomAddress(),
m_icomTGEnable(0U),
m_icomTGDisable(0U),
m_icomDebug(false),
m_kenwoodEnabled(false),
m_kenwoodAddress(),
m_kenwoodTGEnable(0U),
m_kenwoodTGDisable(0U),
m_kenwoodDebug(false)
{
}
CConf::~CConf()
{
}
bool CConf::read()
{
FILE* fp = ::fopen(m_file.c_str(), "rt");
if (fp == NULL) {
::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str());
return false;
}
SECTION section = SECTION_NONE;
char buffer[BUFFER_SIZE];
while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) {
if (buffer[0U] == '#')
continue;
if (buffer[0U] == '[') {
if (::strncmp(buffer, "[General]", 9U) == 0)
section = SECTION_GENERAL;
else if (::strncmp(buffer, "[Id Lookup]", 11U) == 0)
section = SECTION_ID_LOOKUP;
else if (::strncmp(buffer, "[Log]", 5U) == 0)
section = SECTION_LOG;
else if (::strncmp(buffer, "[Network]", 9U) == 0)
section = SECTION_NETWORK;
else if (::strncmp(buffer, "[Icom Network]", 14U) == 0)
section = SECTION_ICOM_NETWORK;
else if (::strncmp(buffer, "[Kenwood Network]", 17U) == 0)
section = SECTION_KENWOOD_NETWORK;
else
section = SECTION_NONE;
continue;
}
char* key = ::strtok(buffer, " \t=\r\n");
if (key == NULL)
continue;
char* value = ::strtok(NULL, "\r\n");
if (value == NULL)
continue;
// Remove quotes from the value
size_t len = ::strlen(value);
if (len > 1U && *value == '"' && value[len - 1U] == '"') {
value[len - 1U] = '\0';
value++;
} else {
char *p;
// if value is not quoted, remove after # (to make comment)
if ((p = strchr(value, '#')) != NULL)
*p = '\0';
// remove trailing tab/space
for (p = value + strlen(value) - 1U; p >= value && (*p == '\t' || *p == ' '); p--)
*p = '\0';
}
if (section == SECTION_GENERAL) {
if (::strcmp(key, "Daemon") == 0)
m_daemon = ::atoi(value) == 1;
else if (::strcmp(key, "TG") == 0)
m_tg = (unsigned short)::atoi(value);
} else if (section == SECTION_ID_LOOKUP) {
if (::strcmp(key, "Name") == 0)
m_lookupName = value;
else if (::strcmp(key, "Time") == 0)
m_lookupTime = (unsigned int)::atoi(value);
} else if (section == SECTION_LOG) {
if (::strcmp(key, "FilePath") == 0)
m_logFilePath = value;
else if (::strcmp(key, "FileRoot") == 0)
m_logFileRoot = value;
else if (::strcmp(key, "FileLevel") == 0)
m_logFileLevel = (unsigned int)::atoi(value);
else if (::strcmp(key, "DisplayLevel") == 0)
m_logDisplayLevel = (unsigned int)::atoi(value);
else if (::strcmp(key, "FileRotate") == 0)
m_logFileRotate = ::atoi(value) == 1;
} else if (section == SECTION_NETWORK) {
if (::strcmp(key, "Port") == 0)
m_networkPort = (unsigned short)::atoi(value);
else if (::strcmp(key, "Debug") == 0)
m_networkDebug = ::atoi(value) == 1;
} else if (section == SECTION_ICOM_NETWORK) {
if (::strcmp(key, "Enabled") == 0)
m_icomEnabled = ::atoi(value) == 1;
else if (::strcmp(key, "Address") == 0)
m_icomAddress = value;
else if (::strcmp(key, "TGEnable") == 0)
m_icomTGEnable = (unsigned short)::atoi(value);
else if (::strcmp(key, "TGDisable") == 0)
m_icomTGDisable = (unsigned short)::atoi(value);
else if (::strcmp(key, "Debug") == 0)
m_icomDebug = ::atoi(value) == 1;
} else if (section == SECTION_KENWOOD_NETWORK) {
if (::strcmp(key, "Enabled") == 0)
m_kenwoodEnabled = ::atoi(value) == 1;
else if (::strcmp(key, "Address") == 0)
m_kenwoodAddress = value;
else if (::strcmp(key, "TGEnable") == 0)
m_kenwoodTGEnable = (unsigned short)::atoi(value);
else if (::strcmp(key, "TGDisable") == 0)
m_kenwoodTGDisable = (unsigned short)::atoi(value);
else if (::strcmp(key, "Debug") == 0)
m_kenwoodDebug = ::atoi(value) == 1;
}
}
::fclose(fp);
return true;
}
bool CConf::getDaemon() const
{
return m_daemon;
}
unsigned short CConf::getTG() const
{
return m_tg;
}
std::string CConf::getLookupName() const
{
return m_lookupName;
}
unsigned int CConf::getLookupTime() const
{
return m_lookupTime;
}
unsigned int CConf::getLogDisplayLevel() const
{
return m_logDisplayLevel;
}
unsigned int CConf::getLogFileLevel() const
{
return m_logFileLevel;
}
std::string CConf::getLogFilePath() const
{
return m_logFilePath;
}
std::string CConf::getLogFileRoot() const
{
return m_logFileRoot;
}
bool CConf::getLogFileRotate() const
{
return m_logFileRotate;
}
unsigned short CConf::getNetworkPort() const
{
return m_networkPort;
}
bool CConf::getNetworkDebug() const
{
return m_networkDebug;
}
bool CConf::getIcomEnabled() const
{
return m_icomEnabled;
}
std::string CConf::getIcomAddress() const
{
return m_icomAddress;
}
unsigned short CConf::getIcomTGEnable() const
{
return m_icomTGEnable;
}
unsigned short CConf::getIcomTGDisable() const
{
return m_icomTGDisable;
}
bool CConf::getIcomDebug() const
{
return m_icomDebug;
}
bool CConf::getKenwoodEnabled() const
{
return m_kenwoodEnabled;
}
std::string CConf::getKenwoodAddress() const
{
return m_kenwoodAddress;
}
unsigned short CConf::getKenwoodTGEnable() const
{
return m_kenwoodTGEnable;
}
unsigned short CConf::getKenwoodTGDisable() const
{
return m_kenwoodTGDisable;
}
bool CConf::getKenwoodDebug() const
{
return m_kenwoodDebug;
}

View File

@ -1,96 +0,0 @@
/*
* Copyright (C) 2015,2016,2018,2020 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(CONF_H)
#define CONF_H
#include <string>
#include <vector>
class CConf
{
public:
CConf(const std::string& file);
~CConf();
bool read();
// The General section
unsigned short getTG() const;
bool getDaemon() const;
// The Id Lookup section
std::string getLookupName() const;
unsigned int getLookupTime() const;
// The Log section
unsigned int getLogDisplayLevel() const;
unsigned int getLogFileLevel() const;
std::string getLogFilePath() const;
std::string getLogFileRoot() const;
bool getLogFileRotate() const;
// The Network section
unsigned short getNetworkPort() const;
bool getNetworkDebug() const;
// The Icom Network section
bool getIcomEnabled() const;
std::string getIcomAddress() const;
unsigned short getIcomTGEnable() const;
unsigned short getIcomTGDisable() const;
bool getIcomDebug() const;
// The Kenwood Network section
bool getKenwoodEnabled() const;
std::string getKenwoodAddress() const;
unsigned short getKenwoodTGEnable() const;
unsigned short getKenwoodTGDisable() const;
bool getKenwoodDebug() const;
private:
std::string m_file;
unsigned short m_tg;
bool m_daemon;
std::string m_lookupName;
unsigned int m_lookupTime;
unsigned int m_logDisplayLevel;
unsigned int m_logFileLevel;
std::string m_logFilePath;
std::string m_logFileRoot;
bool m_logFileRotate;
unsigned short m_networkPort;
bool m_networkDebug;
bool m_icomEnabled;
std::string m_icomAddress;
unsigned short m_icomTGEnable;
unsigned short m_icomTGDisable;
bool m_icomDebug;
bool m_kenwoodEnabled;
std::string m_kenwoodAddress;
unsigned short m_kenwoodTGEnable;
unsigned short m_kenwoodTGDisable;
bool m_kenwoodDebug;
};
#endif

View File

@ -1,139 +0,0 @@
/*
* Copyright (C) 2009-2014,2016,2018,2020,2021 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "IcomNetwork.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned int BUFFER_LENGTH = 200U;
const unsigned int ICOM_PORT = 41300U;
CIcomNetwork::CIcomNetwork(const std::string& address, bool debug) :
m_socket(ICOM_PORT),
m_addr(),
m_addrLen(0U),
m_debug(debug)
{
assert(!address.empty());
if (CUDPSocket::lookup(address, ICOM_PORT, m_addr, m_addrLen) != 0)
m_addrLen = 0U;
}
CIcomNetwork::~CIcomNetwork()
{
}
bool CIcomNetwork::open()
{
if (m_addrLen == 0U) {
LogError("Unable to resolve the address of the Icom network");
return false;
}
bool ret = m_socket.open(m_addr);
if (!ret) {
LogError("Unable to open the Icom network connection");
return false;
}
LogMessage("Opened the Icom network connection");
return true;
}
bool CIcomNetwork::write(const unsigned char* data, unsigned int len)
{
assert(data != NULL);
unsigned char buffer[110U];
::memset(buffer, 0x00U, 110U);
buffer[0U] = 'I';
buffer[1U] = 'C';
buffer[2U] = 'O';
buffer[3U] = 'M';
buffer[4U] = 0x01U;
buffer[5U] = 0x01U;
buffer[6U] = 0x08U;
buffer[7U] = 0xE0U;
if ((data[9U] & 0x02U) == 0x02U) {
buffer[37U] = 0x23U;
buffer[38U] = 0x02U;
buffer[39U] = 0x18U;
} else {
buffer[37U] = 0x23U;
buffer[38U] = (data[9U] & 0x0CU) != 0x00U ? 0x1CU : 0x10U;
buffer[39U] = 0x21U;
}
::memcpy(buffer + 40U, data + 10U, 33U);
if (m_debug)
CUtils::dump(1U, "Icom Network Data Sent", buffer, 102U);
return m_socket.write(buffer, 102U, m_addr, m_addrLen);
}
unsigned int CIcomNetwork::read(unsigned char* data)
{
unsigned char buffer[BUFFER_LENGTH];
sockaddr_storage addr;
unsigned int addrLen;
int length = m_socket.read(buffer, BUFFER_LENGTH, addr, addrLen);
if (length <= 0)
return 0U;
// Check if the data is for us
if (!CUDPSocket::match(m_addr, addr)) {
LogMessage("Icom packet received from an invalid source");
return 0U;
}
// Invalid packet type?
if (::memcmp(buffer, "ICOM", 4U) != 0)
return 0U;
if (length != 102)
return 0U;
if (m_debug)
CUtils::dump(1U, "Icom Network Data Received", buffer, length);
::memcpy(data, buffer + 40U, 33U);
return 33U;
}
void CIcomNetwork::clock(unsigned int ms)
{
}
void CIcomNetwork::close()
{
m_socket.close();
LogMessage("Closing Icom network connection");
}

View File

@ -1,50 +0,0 @@
/*
* Copyright (C) 2009-2014,2016,2018,2020 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef IcomNetwork_H
#define IcomNetwork_H
#include "UDPSocket.h"
#include "Timer.h"
#include <cstdint>
#include <string>
class CIcomNetwork {
public:
CIcomNetwork(const std::string& address, bool debug);
~CIcomNetwork();
bool open();
bool write(const unsigned char* data, unsigned int len);
unsigned int read(unsigned char* data);
void close();
void clock(unsigned int ms);
private:
CUDPSocket m_socket;
sockaddr_storage m_addr;
unsigned int m_addrLen;
bool m_debug;
};
#endif

View File

@ -1,941 +0,0 @@
/*
* Copyright (C) 2009-2014,2016,2018,2020,2021 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "KenwoodNetwork.h"
#include "NXDNCRC.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <ctime>
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
const unsigned int BUFFER_LENGTH = 200U;
const unsigned int RTP_PORT = 64000U;
const unsigned int RTCP_PORT = 64001U;
CKenwoodNetwork::CKenwoodNetwork(const std::string& address, bool debug) :
m_rtpSocket(RTP_PORT),
m_rtcpSocket(RTCP_PORT),
m_rtpAddr(),
m_rtpAddrLen(0U),
m_rtcpAddr(),
m_rtcpAddrLen(0U),
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_ssrc(0U),
m_debug(debug),
m_startSecs(0U),
m_startUSecs(0U),
m_rtcpTimer(1000U, 0U, 200U),
m_hangTimer(1000U, 5U),
m_hangType(0U),
m_hangSrc(0U),
m_hangDst(0U),
m_random()
{
assert(!address.empty());
m_sacch = new unsigned char[10U];
if (CUDPSocket::lookup(address, RTP_PORT, m_rtpAddr, m_rtpAddrLen) != 0)
m_rtpAddrLen = 0U;
if (CUDPSocket::lookup(address, RTCP_PORT, m_rtcpAddr, m_rtcpAddrLen) != 0)
m_rtcpAddrLen = 0U;
std::random_device rd;
std::mt19937 mt(rd());
m_random = mt;
}
CKenwoodNetwork::~CKenwoodNetwork()
{
delete[] m_sacch;
}
bool CKenwoodNetwork::open()
{
if (m_rtpAddrLen == 0U || m_rtcpAddrLen == 0U) {
LogError("Unable to resolve the address of the Kenwood network");
return false;
}
if (!m_rtcpSocket.open(m_rtcpAddr)) {
LogError("Unable to open the Kenwood network connection");
return false;
}
if (!m_rtpSocket.open(m_rtpAddr)) {
LogError("Unable to open the Kenwood network connection");
m_rtcpSocket.close();
return false;
}
LogMessage("Opened the Kenwood network connection");
std::uniform_int_distribution<unsigned int> dist(0x00000001, 0xfffffffe);
m_ssrc = dist(m_random);
return true;
}
bool CKenwoodNetwork::write(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
switch (data[0U]) {
case 0x81U: // Voice header or trailer
case 0x83U:
return processIcomVoiceHeader(data);
case 0xACU: // Voice data
case 0xAEU:
return processIcomVoiceData(data);
default:
return false;
}
}
bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData)
{
assert(inData != NULL);
unsigned char outData[30U];
::memset(outData, 0x00U, 30U);
// SACCH
outData[0U] = inData[2U];
outData[1U] = inData[1U];
outData[2U] = inData[4U] & 0xC0U;
outData[3U] = inData[3U];
// FACCH 1+2
outData[4U] = outData[14U] = inData[6U];
outData[5U] = outData[15U] = inData[5U];
outData[6U] = outData[16U] = inData[8U];
outData[7U] = outData[17U] = inData[7U];
outData[8U] = outData[18U] = inData[10U];
outData[9U] = outData[19U] = inData[9U];
outData[10U] = outData[20U] = inData[12U];
outData[11U] = outData[21U] = inData[11U];
unsigned short src = (inData[8U] << 8) + (inData[9U] << 0);
unsigned short dst = (inData[10U] << 8) + (inData[11U] << 0);
unsigned char type = (inData[7U] >> 5) & 0x07U;
switch (inData[5U] & 0x3FU) {
case 0x01U:
m_hangTimer.stop();
m_rtcpTimer.start();
writeRTCPStart();
return writeRTPVoiceHeader(outData);
case 0x08U: {
m_hangTimer.start();
bool ret = writeRTPVoiceTrailer(outData);
writeRTCPHang(type, src, dst);
return ret;
}
default:
return false;
}
}
bool CKenwoodNetwork::processIcomVoiceData(const unsigned char* inData)
{
assert(inData != NULL);
unsigned char outData[40U], temp[10U];
::memset(outData, 0x00U, 40U);
// SACCH
outData[0U] = inData[2U];
outData[1U] = inData[1U];
outData[2U] = inData[4U] & 0xC0U;
outData[3U] = inData[3U];
// Audio 1
::memset(temp, 0x00U, 10U);
for (unsigned int i = 0U; i < 49U; i++) {
unsigned int offset = (5U * 8U) + i;
bool b = READ_BIT(inData, offset);
WRITE_BIT(temp, i, b);
}
outData[4U] = temp[1U];
outData[5U] = temp[0U];
outData[6U] = temp[3U];
outData[7U] = temp[2U];
outData[8U] = temp[5U];
outData[9U] = temp[4U];
outData[10U] = temp[7U];
outData[11U] = temp[6U];
// Audio 2
::memset(temp, 0x00U, 10U);
for (unsigned int i = 0U; i < 49U; i++) {
unsigned int offset = (5U * 8U) + 49U + i;
bool b = READ_BIT(inData, offset);
WRITE_BIT(temp, i, b);
}
outData[12U] = temp[1U];
outData[13U] = temp[0U];
outData[14U] = temp[3U];
outData[15U] = temp[2U];
outData[16U] = temp[5U];
outData[17U] = temp[4U];
outData[18U] = temp[7U];
outData[19U] = temp[6U];
// Audio 3
::memset(temp, 0x00U, 10U);
for (unsigned int i = 0U; i < 49U; i++) {
unsigned int offset = (19U * 8U) + i;
bool b = READ_BIT(inData, offset);
WRITE_BIT(temp, i, b);
}
outData[20U] = temp[1U];
outData[21U] = temp[0U];
outData[22U] = temp[3U];
outData[23U] = temp[2U];
outData[24U] = temp[5U];
outData[25U] = temp[4U];
outData[26U] = temp[7U];
outData[27U] = temp[6U];
// Audio 4
::memset(temp, 0x00U, 10U);
for (unsigned int i = 0U; i < 49U; i++) {
unsigned int offset = (19U * 8U) + 49U + i;
bool b = READ_BIT(inData, offset);
WRITE_BIT(temp, i, b);
}
outData[28U] = temp[1U];
outData[29U] = temp[0U];
outData[30U] = temp[3U];
outData[31U] = temp[2U];
outData[32U] = temp[5U];
outData[33U] = temp[4U];
outData[34U] = temp[7U];
outData[35U] = temp[6U];
return writeRTPVoiceData(outData);
}
bool CKenwoodNetwork::writeRTPVoiceHeader(const unsigned char* data)
{
assert(data != NULL);
unsigned char buffer[50U];
::memset(buffer, 0x00U, 50U);
buffer[0U] = 0x80U;
buffer[1U] = 0x66U;
m_seqNo++;
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
unsigned long timeStamp = getTimeStamp();
buffer[4U] = (timeStamp >> 24) & 0xFFU;
buffer[5U] = (timeStamp >> 16) & 0xFFU;
buffer[6U] = (timeStamp >> 8) & 0xFFU;
buffer[7U] = (timeStamp >> 0) & 0xFFU;
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
buffer[10U] = (m_ssrc >> 8) & 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[17U] = 0x03U;
buffer[18U] = 0x04U;
buffer[19U] = 0x04U;
buffer[20U] = 0x0AU;
buffer[21U] = 0x05U;
buffer[22U] = 0x0AU;
::memcpy(buffer + 23U, data, 24U);
if (m_debug)
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 47U);
return m_rtpSocket.write(buffer, 47U, m_rtpAddr, m_rtpAddrLen);
}
bool CKenwoodNetwork::writeRTPVoiceTrailer(const unsigned char* data)
{
assert(data != NULL);
unsigned char buffer[50U];
::memset(buffer, 0x00U, 50U);
buffer[0U] = 0x80U;
buffer[1U] = 0x66U;
m_seqNo++;
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
unsigned long timeStamp = getTimeStamp();
buffer[4U] = (timeStamp >> 24) & 0xFFU;
buffer[5U] = (timeStamp >> 16) & 0xFFU;
buffer[6U] = (timeStamp >> 8) & 0xFFU;
buffer[7U] = (timeStamp >> 0) & 0xFFU;
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
buffer[12U] = m_sessionId;
buffer[13U] = 0x00U;
buffer[14U] = 0x00U;
buffer[15U] = 0x00U;
buffer[16U] = 0x03U;
buffer[17U] = 0x03U;
buffer[18U] = 0x04U;
buffer[19U] = 0x04U;
buffer[20U] = 0x0AU;
buffer[21U] = 0x05U;
buffer[22U] = 0x0AU;
::memcpy(buffer + 23U, data, 24U);
if (m_debug)
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 47U);
return m_rtpSocket.write(buffer, 47U, m_rtpAddr, m_rtpAddrLen);
}
bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data)
{
assert(data != NULL);
unsigned char buffer[60U];
::memset(buffer, 0x00U, 60U);
buffer[0U] = 0x80U;
buffer[1U] = 0x66U;
m_seqNo++;
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
unsigned long timeStamp = getTimeStamp();
buffer[4U] = (timeStamp >> 24) & 0xFFU;
buffer[5U] = (timeStamp >> 16) & 0xFFU;
buffer[6U] = (timeStamp >> 8) & 0xFFU;
buffer[7U] = (timeStamp >> 0) & 0xFFU;
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
buffer[12U] = m_sessionId;
buffer[13U] = 0x00U;
buffer[14U] = 0x00U;
buffer[15U] = 0x00U;
buffer[16U] = 0x03U;
buffer[17U] = 0x02U;
buffer[18U] = 0x04U;
buffer[19U] = 0x07U;
buffer[20U] = 0x10U;
buffer[21U] = 0x08U;
buffer[22U] = 0x10U;
::memcpy(buffer + 23U, data, 36U);
if (m_debug)
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 59U);
return m_rtpSocket.write(buffer, 59U, m_rtpAddr, m_rtpAddrLen);
}
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];
::memset(buffer, 0x00U, 30U);
buffer[0U] = 0x8AU;
buffer[1U] = 0xCCU;
buffer[2U] = 0x00U;
buffer[3U] = 0x06U;
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_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[24U] = 0x01U;
buffer[27U] = 0x0AU;
if (m_debug)
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
return m_rtcpSocket.write(buffer, 28U, m_rtcpAddr, m_rtcpAddrLen);
}
bool CKenwoodNetwork::writeRTCPPing()
{
unsigned char buffer[30U];
::memset(buffer, 0x00U, 30U);
buffer[0U] = 0x8AU;
buffer[1U] = 0xCCU;
buffer[2U] = 0x00U;
buffer[3U] = 0x06U;
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_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[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_rtcpAddr, m_rtcpAddrLen);
}
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)
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U);
return m_rtcpSocket.write(buffer, 20U, m_rtcpAddr, m_rtcpAddrLen);
}
unsigned int CKenwoodNetwork::read(unsigned char* data)
{
assert(data != NULL);
unsigned char dummy[BUFFER_LENGTH];
readRTCP(dummy);
unsigned int len = readRTP(data);
switch (len) {
case 0U: // Nothing received
return 0U;
case 35U: // Voice header or trailer
return processKenwoodVoiceHeader(data);
case 47U: // Voice data
if (m_headerSeen)
return processKenwoodVoiceData(data);
else
return processKenwoodVoiceLateEntry(data);
case 31U: // Data
return processKenwoodData(data);
default:
CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len);
return 0U;
}
}
unsigned int CKenwoodNetwork::readRTP(unsigned char* data)
{
assert(data != NULL);
unsigned char buffer[BUFFER_LENGTH];
sockaddr_storage addr;
unsigned int addrLen;
int length = m_rtpSocket.read(buffer, BUFFER_LENGTH, addr, addrLen);
if (length <= 0)
return 0U;
// Check if the data is for us
if (!CUDPSocket::match(m_rtpAddr, addr, IMT_ADDRESS_ONLY)) {
LogMessage("Kenwood RTP packet received from an invalid source");
return 0U;
}
if (m_debug)
CUtils::dump(1U, "Kenwood Network RTP Data Received", buffer, length);
::memcpy(data, buffer + 12U, length - 12U);
return length - 12U;
}
unsigned int CKenwoodNetwork::readRTCP(unsigned char* data)
{
assert(data != NULL);
unsigned char buffer[BUFFER_LENGTH];
sockaddr_storage addr;
unsigned int addrLen;
int length = m_rtcpSocket.read(buffer, BUFFER_LENGTH, addr, addrLen);
if (length <= 0)
return 0U;
// Check if the data is for us
if (!CUDPSocket::match(m_rtcpAddr, addr, IMT_ADDRESS_ONLY)) {
LogMessage("Kenwood RTCP packet received from an invalid source");
return 0U;
}
if (m_debug)
CUtils::dump(1U, "Kenwood Network RTCP Data Received", buffer, length);
if (::memcmp(buffer + 8U, "KWNE", 4U) != 0) {
LogError("Missing RTCP KWNE signature");
return 0U;
}
::memcpy(data, buffer + 12U, length - 12U);
return length - 12U;
}
void CKenwoodNetwork::close()
{
m_rtcpSocket.close();
m_rtpSocket.close();
LogMessage("Closing Kenwood connection");
}
void CKenwoodNetwork::clock(unsigned int ms)
{
m_rtcpTimer.clock(ms);
if (m_rtcpTimer.isRunning() && m_rtcpTimer.hasExpired()) {
if (m_hangTimer.isRunning())
writeRTCPHang();
else
writeRTCPPing();
m_rtcpTimer.start();
}
m_hangTimer.clock(ms);
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
m_rtcpTimer.stop();
m_hangTimer.stop();
}
}
unsigned int CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
{
assert(inData != NULL);
unsigned char outData[50U], temp[20U];
::memset(outData, 0x00U, 50U);
// LICH
outData[0U] = 0x83U;
// SACCH
::memset(temp, 0x00U, 20U);
temp[0U] = inData[12U];
temp[1U] = inData[11U];
temp[2U] = inData[14U];
temp[3U] = inData[13U];
CNXDNCRC::encodeCRC6(temp, 26U);
::memcpy(outData + 1U, temp, 4U);
// FACCH 1+2
::memset(temp, 0x00U, 20U);
temp[0U] = inData[16U];
temp[1U] = inData[15U];
temp[2U] = inData[18U];
temp[3U] = inData[17U];
temp[4U] = inData[20U];
temp[5U] = inData[19U];
temp[6U] = inData[22U];
temp[7U] = inData[21U];
temp[8U] = inData[24U];
temp[9U] = inData[23U];
CNXDNCRC::encodeCRC12(temp, 80U);
::memcpy(outData + 5U, temp, 12U);
::memcpy(outData + 19U, temp, 12U);
switch (outData[5U] & 0x3FU) {
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:
::memcpy(inData, outData, 33U);
m_headerSeen = false;
m_seen1 = false;
m_seen2 = false;
m_seen3 = false;
m_seen4 = false;
return 33U;
default:
return 0U;
}
}
unsigned int CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
{
assert(inData != NULL);
unsigned char outData[50U], temp[20U];
::memset(outData, 0x00U, 50U);
// LICH
outData[0U] = 0xAEU;
// SACCH
::memset(temp, 0x00U, 20U);
temp[0U] = inData[12U];
temp[1U] = inData[11U];
temp[2U] = inData[14U];
temp[3U] = inData[13U];
CNXDNCRC::encodeCRC6(temp, 26U);
::memcpy(outData + 1U, temp, 4U);
// AMBE 1+2
unsigned int n = 5U * 8U;
temp[0U] = inData[16U];
temp[1U] = inData[15U];
temp[2U] = inData[18U];
temp[3U] = inData[17U];
temp[4U] = inData[20U];
temp[5U] = inData[19U];
temp[6U] = inData[22U];
temp[7U] = inData[21U];
for (unsigned int i = 0U; i < 49U; i++, n++) {
bool b = READ_BIT(temp, i);
WRITE_BIT(outData, n, b);
}
temp[0U] = inData[24U];
temp[1U] = inData[23U];
temp[2U] = inData[26U];
temp[3U] = inData[25U];
temp[4U] = inData[28U];
temp[5U] = inData[27U];
temp[6U] = inData[30U];
temp[7U] = inData[29U];
for (unsigned int i = 0U; i < 49U; i++, n++) {
bool b = READ_BIT(temp, i);
WRITE_BIT(outData, n, b);
}
// AMBE 3+4
n = 19U * 8U;
temp[0U] = inData[32U];
temp[1U] = inData[31U];
temp[2U] = inData[34U];
temp[3U] = inData[33U];
temp[4U] = inData[36U];
temp[5U] = inData[35U];
temp[6U] = inData[38U];
temp[7U] = inData[37U];
for (unsigned int i = 0U; i < 49U; i++, n++) {
bool b = READ_BIT(temp, i);
WRITE_BIT(outData, n, b);
}
temp[0U] = inData[40U];
temp[1U] = inData[39U];
temp[2U] = inData[42U];
temp[3U] = inData[41U];
temp[4U] = inData[44U];
temp[5U] = inData[43U];
temp[6U] = inData[46U];
temp[7U] = inData[45U];
for (unsigned int i = 0U; i < 49U; i++, n++) {
bool b = READ_BIT(temp, i);
WRITE_BIT(outData, n, b);
}
::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