diff --git a/NXDNReflector/Conf.cpp b/NXDNReflector/Conf.cpp index 0564d15..f1e1cf8 100644 --- a/NXDNReflector/Conf.cpp +++ b/NXDNReflector/Conf.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * 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 @@ -48,6 +48,7 @@ m_logFileRoot(), m_networkPort(0U), m_networkDebug(false), m_nxCoreEnabled(false), +m_nxCoreProtocol("Icom"), m_nxCoreAddress(), m_nxCoreTGEnable(0U), m_nxCoreTGDisable(0U), @@ -123,6 +124,8 @@ bool CConf::read() } else if (section == SECTION_NXCORE) { if (::strcmp(key, "Enabled") == 0) m_nxCoreEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Protocol") == 0) + m_nxCoreProtocol = value; else if (::strcmp(key, "Address") == 0) m_nxCoreAddress = value; else if (::strcmp(key, "TGEnable") == 0) @@ -194,6 +197,11 @@ bool CConf::getNXCoreEnabled() const return m_nxCoreEnabled; } +std::string CConf::getNXCoreProtocol() const +{ + return m_nxCoreProtocol; +} + std::string CConf::getNXCoreAddress() const { return m_nxCoreAddress; diff --git a/NXDNReflector/Conf.h b/NXDNReflector/Conf.h index d5c4c2e..57156ed 100644 --- a/NXDNReflector/Conf.h +++ b/NXDNReflector/Conf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * 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 @@ -50,6 +50,7 @@ public: // The NXCore section bool getNXCoreEnabled() const; + std::string getNXCoreProtocol() const; std::string getNXCoreAddress() const; unsigned short getNXCoreTGEnable() const; unsigned short getNXCoreTGDisable() const; @@ -72,6 +73,7 @@ private: bool m_networkDebug; bool m_nxCoreEnabled; + std::string m_nxCoreProtocol; std::string m_nxCoreAddress; unsigned short m_nxCoreTGEnable; unsigned short m_nxCoreTGDisable; diff --git a/NXDNReflector/CoreNetwork.cpp b/NXDNReflector/CoreNetwork.cpp new file mode 100644 index 0000000..c842dc7 --- /dev/null +++ b/NXDNReflector/CoreNetwork.cpp @@ -0,0 +1,23 @@ +/* + * 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. + */ + +#include "CoreNetwork.h" + +ICoreNetwork::~ICoreNetwork() +{ +} diff --git a/NXDNReflector/CoreNetwork.h b/NXDNReflector/CoreNetwork.h new file mode 100644 index 0000000..d991b81 --- /dev/null +++ b/NXDNReflector/CoreNetwork.h @@ -0,0 +1,55 @@ +/* + * 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 CoreNetwork_H +#define CoreNetwork_H + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +#include +#include + +class ICoreNetwork { +public: + virtual ~ICoreNetwork() = 0; + + virtual bool open() = 0; + + virtual bool write(const unsigned char* data, unsigned int length) = 0; + + virtual unsigned int read(unsigned char* data) = 0; + + virtual void close() = 0; + + virtual void clock(unsigned int ms) = 0; + +private: +}; + +#endif diff --git a/NXDNReflector/NXCoreNetwork.cpp b/NXDNReflector/IcomNetwork.cpp similarity index 67% rename from NXDNReflector/NXCoreNetwork.cpp rename to NXDNReflector/IcomNetwork.cpp index e676710..08d68ec 100644 --- a/NXDNReflector/NXCoreNetwork.cpp +++ b/NXDNReflector/IcomNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * 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 @@ -16,7 +16,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "NXCoreNetwork.h" +#include "IcomNetwork.h" #include "Utils.h" #include "Log.h" @@ -26,10 +26,10 @@ const unsigned int BUFFER_LENGTH = 200U; -const unsigned int NXCORE_PORT = 41300U; +const unsigned int ICOM_PORT = 41300U; -CNXCoreNetwork::CNXCoreNetwork(const std::string& address, bool debug) : -m_socket(NXCORE_PORT), +CIcomNetwork::CIcomNetwork(const std::string& address, bool debug) : +m_socket(ICOM_PORT), m_address(), m_debug(debug) { @@ -38,13 +38,13 @@ m_debug(debug) m_address = CUDPSocket::lookup(address); } -CNXCoreNetwork::~CNXCoreNetwork() +CIcomNetwork::~CIcomNetwork() { } -bool CNXCoreNetwork::open() +bool CIcomNetwork::open() { - LogMessage("Opening NXCore network connection"); + LogMessage("Opening Icom network connection"); if (m_address.s_addr == INADDR_NONE) return false; @@ -52,7 +52,7 @@ bool CNXCoreNetwork::open() return m_socket.open(); } -bool CNXCoreNetwork::write(const unsigned char* data, unsigned int len) +bool CIcomNetwork::write(const unsigned char* data, unsigned int len) { assert(data != NULL); @@ -81,12 +81,12 @@ bool CNXCoreNetwork::write(const unsigned char* data, unsigned int len) ::memcpy(buffer + 40U, data + 10U, 33U); if (m_debug) - CUtils::dump(1U, "NXCore Network Data Sent", buffer, 102U); + CUtils::dump(1U, "Icom Network Data Sent", buffer, 102U); - return m_socket.write(buffer, 102U, m_address, NXCORE_PORT); + return m_socket.write(buffer, 102U, m_address, ICOM_PORT); } -unsigned int CNXCoreNetwork::read(unsigned char* data) +unsigned int CIcomNetwork::read(unsigned char* data) { unsigned char buffer[BUFFER_LENGTH]; @@ -97,8 +97,8 @@ unsigned int CNXCoreNetwork::read(unsigned char* data) return 0U; // Check if the data is for us - if (m_address.s_addr != address.s_addr || port != NXCORE_PORT) { - LogMessage("NXCore packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, NXCORE_PORT, port); + if (m_address.s_addr != address.s_addr || port != ICOM_PORT) { + LogMessage("Icom packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, ICOM_PORT, port); return 0U; } @@ -110,16 +110,20 @@ unsigned int CNXCoreNetwork::read(unsigned char* data) return 0U; if (m_debug) - CUtils::dump(1U, "NXCore Network Data Received", buffer, length); + CUtils::dump(1U, "Icom Network Data Received", buffer, length); ::memcpy(data, buffer + 40U, 33U); return 33U; } -void CNXCoreNetwork::close() +void CIcomNetwork::clock(unsigned int ms) +{ +} + +void CIcomNetwork::close() { m_socket.close(); - LogMessage("Closing NXCore network connection"); + LogMessage("Closing Icom network connection"); } diff --git a/NXDNReflector/NXCoreNetwork.h b/NXDNReflector/IcomNetwork.h similarity index 66% rename from NXDNReflector/NXCoreNetwork.h rename to NXDNReflector/IcomNetwork.h index 78c896c..704b12b 100644 --- a/NXDNReflector/NXCoreNetwork.h +++ b/NXDNReflector/IcomNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * 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 @@ -16,27 +16,30 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef NXCoreNetwork_H -#define NXCoreNetwork_H +#ifndef IcomNetwork_H +#define IcomNetwork_H +#include "CoreNetwork.h" #include "UDPSocket.h" #include "Timer.h" #include #include -class CNXCoreNetwork { +class CIcomNetwork : public ICoreNetwork { public: - CNXCoreNetwork(const std::string& address, bool debug); - ~CNXCoreNetwork(); + CIcomNetwork(const std::string& address, bool debug); + virtual ~CIcomNetwork(); - bool open(); + virtual bool open(); - bool write(const unsigned char* data, unsigned int len); + virtual bool write(const unsigned char* data, unsigned int len); - unsigned int read(unsigned char* data); + virtual unsigned int read(unsigned char* data); - void close(); + virtual void close(); + + virtual void clock(unsigned int ms); private: CUDPSocket m_socket; diff --git a/NXDNReflector/KenwoodNetwork.cpp b/NXDNReflector/KenwoodNetwork.cpp new file mode 100644 index 0000000..979c021 --- /dev/null +++ b/NXDNReflector/KenwoodNetwork.cpp @@ -0,0 +1,645 @@ +/* + * 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. + */ + +#include "KenwoodNetwork.h" +#include "NXDNCRC.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +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_rtcpSocket(RTCP_PORT), +m_rtpSocket(RTP_PORT), +m_stopWatch(), +m_address(), +m_seqNo(0U), +m_timeStamp(0U), +m_ssrc(0U), +m_debug(debug), +m_timer(1000U, 0U, 200U) +{ + assert(!address.empty()); + + m_address = CUDPSocket::lookup(address); + + ::srand((unsigned int)m_stopWatch.time()); +} + +CKenwoodNetwork::~CKenwoodNetwork() +{ +} + +bool CKenwoodNetwork::open() +{ + LogMessage("Opening Kenwood connection"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + if (!m_rtcpSocket.open()) + return false; + + if (!m_rtpSocket.open()) { + m_rtcpSocket.close(); + return false; + } + + m_ssrc = ::rand(); + + 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_timer.start(); + writeRTCPData(type, src, dst); + return writeRTPVoiceHeader(outData); + case 0x08U: + m_timer.stop(); + writeRTCPData(type, src, dst); + return writeRTPVoiceTrailer(outData); + 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; + + buffer[2U] = (m_seqNo >> 8) & 0xFFU; + buffer[3U] = (m_seqNo >> 0) & 0xFFU; + m_seqNo++; + + m_timeStamp = (unsigned long)m_stopWatch.time(); + + buffer[4U] = (m_timeStamp >> 24) & 0xFFU; + buffer[5U] = (m_timeStamp >> 16) & 0xFFU; + buffer[6U] = (m_timeStamp >> 8) & 0xFFU; + buffer[7U] = (m_timeStamp >> 0) & 0xFFU; + m_timeStamp += 640U; + + 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[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_address, RTP_PORT); +} + +bool CKenwoodNetwork::writeRTPVoiceTrailer(const unsigned char* data) +{ + assert(data != NULL); + + unsigned char buffer[50U]; + ::memset(buffer, 0x00U, 50U); + + buffer[0U] = 0x80U; + buffer[1U] = 0x66U; + + buffer[2U] = (m_seqNo >> 8) & 0xFFU; + buffer[3U] = (m_seqNo >> 0) & 0xFFU; + + buffer[4U] = (m_timeStamp >> 24) & 0xFFU; + buffer[5U] = (m_timeStamp >> 16) & 0xFFU; + buffer[6U] = (m_timeStamp >> 8) & 0xFFU; + buffer[7U] = (m_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[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_address, RTP_PORT); +} + +bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data) +{ + assert(data != NULL); + + unsigned char buffer[60U]; + ::memset(buffer, 0x00U, 60U); + + buffer[0U] = 0x80U; + buffer[1U] = 0x66U; + + buffer[2U] = (m_seqNo >> 8) & 0xFFU; + buffer[3U] = (m_seqNo >> 0) & 0xFFU; + m_seqNo++; + + buffer[4U] = (m_timeStamp >> 24) & 0xFFU; + buffer[5U] = (m_timeStamp >> 16) & 0xFFU; + buffer[6U] = (m_timeStamp >> 8) & 0xFFU; + buffer[7U] = (m_timeStamp >> 0) & 0xFFU; + m_timeStamp += 640U; + + 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[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_address, RTP_PORT); +} + +bool CKenwoodNetwork::writeRTCPPing() +{ + unsigned char buffer[30U]; + ::memset(buffer, 0x00U, 30U); + + buffer[0U] = 0x8AU; + buffer[1U] = 0xCCU; + + 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[22U] = 0x02U; + + buffer[24U] = 0x01U; + buffer[25U] = 0x01U; + + 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::writeRTCPData(unsigned char type, unsigned short src, unsigned short dst) +{ + unsigned char buffer[20U]; + ::memset(buffer, 0x00U, 20U); + + buffer[0U] = 0x8BU; + buffer[1U] = 0xCCU; + + 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] = (src >> 8) & 0xFFU; + buffer[13U] = (src >> 0) & 0xFFU; + + buffer[14U] = (dst >> 8) & 0xFFU; + buffer[15U] = (dst >> 0) & 0xFFU; + + buffer[16U] = type; + + if (m_debug) + CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U); + + return m_rtcpSocket.write(buffer, 20U, m_address, RTCP_PORT); +} + +unsigned int CKenwoodNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + unsigned char dummy[BUFFER_LENGTH]; + readRTCP(dummy); + + bool ret; + + unsigned int len = readRTP(data); + if (len > 0U) { + switch (data[9U]) { + case 0x05U: // Voice header or trailer + ret = processKenwoodVoiceHeader(data); + if (!ret) + return 0U; + return 33U; + case 0x08U: // Voice data + processKenwoodVoiceData(data); + return 33U; + default: + break; + } + } + + 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]; + + in_addr address; + unsigned int port; + int length = m_rtpSocket.read(buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return 0U; + + // Check if the data is for us + if (m_address.s_addr != address.s_addr || port != RTP_PORT) { + 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); + return 0U; + } + + if (m_debug) + 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); + + return length - 12U; +} + +unsigned int CKenwoodNetwork::readRTCP(unsigned char* data) +{ + assert(data != NULL); + + unsigned char buffer[BUFFER_LENGTH]; + + in_addr address; + unsigned int port; + int length = m_rtcpSocket.read(buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return 0U; + + // Check if the data is for us + if (m_address.s_addr != address.s_addr || port != RTCP_PORT) { + 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); + return 0U; + } + + if (m_debug) + 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) { + 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_timer.clock(ms); + if (m_timer.isRunning() && m_timer.hasExpired()) { + writeRTCPPing(); + m_timer.start(); + } +} + +bool 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: + case 0x08U: + ::memcpy(inData, outData, 33U); + return true; + default: + return false; + } +} + +void 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); +} diff --git a/NXDNReflector/KenwoodNetwork.h b/NXDNReflector/KenwoodNetwork.h new file mode 100644 index 0000000..4c0ded9 --- /dev/null +++ b/NXDNReflector/KenwoodNetwork.h @@ -0,0 +1,69 @@ +/* + * 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 KenwoodNetwork_H +#define KenwoodNetwork_H + +#include "CoreNetwork.h" +#include "StopWatch.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CKenwoodNetwork : public ICoreNetwork { +public: + CKenwoodNetwork(const std::string& address, bool debug); + virtual ~CKenwoodNetwork(); + + virtual bool open(); + + virtual bool write(const unsigned char* data, unsigned int length); + + virtual unsigned int read(unsigned char* data); + + virtual void close(); + + virtual void clock(unsigned int ms); + +private: + CUDPSocket m_rtpSocket; + CUDPSocket m_rtcpSocket; + CStopWatch m_stopWatch; + in_addr m_address; + unsigned short m_seqNo; + unsigned long m_timeStamp; + unsigned int m_ssrc; + bool m_debug; + CTimer m_timer; + + bool processIcomVoiceHeader(const unsigned char* data); + bool processIcomVoiceData(const unsigned char* data); + bool processKenwoodVoiceHeader(unsigned char* data); + void processKenwoodVoiceData(unsigned char* data); + bool writeRTPVoiceHeader(const unsigned char* data); + bool writeRTPVoiceData(const unsigned char* data); + bool writeRTPVoiceTrailer(const unsigned char* data); + bool writeRTCPPing(); + bool writeRTCPData(unsigned char type, unsigned short src, unsigned short dst); + unsigned int readRTP(unsigned char* data); + unsigned int readRTCP(unsigned char* data); +}; + +#endif diff --git a/NXDNReflector/Makefile b/NXDNReflector/Makefile index 8cd4048..43c534b 100644 --- a/NXDNReflector/Makefile +++ b/NXDNReflector/Makefile @@ -4,7 +4,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread LIBS = -lpthread LDFLAGS = -g -OBJECTS = Conf.o Log.o Mutex.o NXCoreNetwork.o NXDNLookup.o NXDNNetwork.o NXDNReflector.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o +OBJECTS = Conf.o CoreNetwork.o IcomNetwork.o KenwoodNetwork.o Log.o Mutex.o NXDNCRC.o NXDNLookup.o NXDNNetwork.o NXDNReflector.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o all: NXDNReflector diff --git a/NXDNReflector/NXDNCRC.cpp b/NXDNReflector/NXDNCRC.cpp new file mode 100644 index 0000000..60278ae --- /dev/null +++ b/NXDNReflector/NXDNCRC.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 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 + * 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 "NXDNCRC.h" + +#include +#include + +const uint8_t BIT_MASK_TABLE1[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE1[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE1[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE1[(i)&7]) + +bool CNXDNCRC::checkCRC6(const unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint8_t crc = createCRC6(in, length); + + uint8_t temp[1U]; + temp[0U] = 0x00U; + unsigned int j = length; + for (unsigned int i = 2U; i < 8U; i++, j++) { + bool b = READ_BIT1(in, j); + WRITE_BIT1(temp, i, b); + } + + return crc == temp[0U]; +} + +void CNXDNCRC::encodeCRC6(unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint8_t crc[1U]; + crc[0U] = createCRC6(in, length); + + unsigned int n = length; + for (unsigned int i = 2U; i < 8U; i++, n++) { + bool b = READ_BIT1(crc, i); + WRITE_BIT1(in, n, b); + } +} + +bool CNXDNCRC::checkCRC12(const unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint16_t crc = createCRC12(in, length); + uint8_t temp1[2U]; + temp1[0U] = (crc >> 8) & 0xFFU; + temp1[1U] = (crc >> 0) & 0xFFU; + + uint8_t temp2[2U]; + temp2[0U] = 0x00U; + temp2[1U] = 0x00U; + unsigned int j = length; + for (unsigned int i = 4U; i < 16U; i++, j++) { + bool b = READ_BIT1(in, j); + WRITE_BIT1(temp2, i, b); + } + + return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U]; +} + +void CNXDNCRC::encodeCRC12(unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint16_t crc = createCRC12(in, length); + + uint8_t temp[2U]; + temp[0U] = (crc >> 8) & 0xFFU; + temp[1U] = (crc >> 0) & 0xFFU; + + unsigned int n = length; + for (unsigned int i = 4U; i < 16U; i++, n++) { + bool b = READ_BIT1(temp, i); + WRITE_BIT1(in, n, b); + } +} + +bool CNXDNCRC::checkCRC15(const unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint16_t crc = createCRC15(in, length); + uint8_t temp1[2U]; + temp1[0U] = (crc >> 8) & 0xFFU; + temp1[1U] = (crc >> 0) & 0xFFU; + + uint8_t temp2[2U]; + temp2[0U] = 0x00U; + temp2[1U] = 0x00U; + unsigned int j = length; + for (unsigned int i = 1U; i < 16U; i++, j++) { + bool b = READ_BIT1(in, j); + WRITE_BIT1(temp2, i, b); + } + + return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U]; +} + +void CNXDNCRC::encodeCRC15(unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint16_t crc = createCRC15(in, length); + + uint8_t temp[2U]; + temp[0U] = (crc >> 8) & 0xFFU; + temp[1U] = (crc >> 0) & 0xFFU; + + unsigned int n = length; + for (unsigned int i = 1U; i < 16U; i++, n++) { + bool b = READ_BIT1(temp, i); + WRITE_BIT1(in, n, b); + } +} + +uint8_t CNXDNCRC::createCRC6(const unsigned char* in, unsigned int length) +{ + uint8_t crc = 0x3FU; + + for (unsigned int i = 0U; i < length; i++) { + bool bit1 = READ_BIT1(in, i) != 0x00U; + bool bit2 = (crc & 0x20U) == 0x20U; + + crc <<= 1; + + if (bit1 ^ bit2) + crc ^= 0x27U; + } + + return crc & 0x3FU; +} + +uint16_t CNXDNCRC::createCRC12(const unsigned char* in, unsigned int length) +{ + uint16_t crc = 0x0FFFU; + + for (unsigned int i = 0U; i < length; i++) { + bool bit1 = READ_BIT1(in, i) != 0x00U; + bool bit2 = (crc & 0x0800U) == 0x0800U; + + crc <<= 1; + + if (bit1 ^ bit2) + crc ^= 0x080FU; + } + + return crc & 0x0FFFU; +} + +uint16_t CNXDNCRC::createCRC15(const unsigned char* in, unsigned int length) +{ + uint16_t crc = 0x7FFFU; + + for (unsigned int i = 0U; i < length; i++) { + bool bit1 = READ_BIT1(in, i) != 0x00U; + bool bit2 = (crc & 0x4000U) == 0x4000U; + + crc <<= 1; + + if (bit1 ^ bit2) + crc ^= 0x4CC5U; + } + + return crc & 0x7FFFU; +} diff --git a/NXDNReflector/NXDNCRC.h b/NXDNReflector/NXDNCRC.h new file mode 100644 index 0000000..12e2e40 --- /dev/null +++ b/NXDNReflector/NXDNCRC.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 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 + * 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(NXDNCRC_H) +#define NXDNCRC_H + +#include + +class CNXDNCRC +{ +public: + static bool checkCRC6(const unsigned char* in, unsigned int length); + static void encodeCRC6(unsigned char* in, unsigned int length); + + static bool checkCRC12(const unsigned char* in, unsigned int length); + static void encodeCRC12(unsigned char* in, unsigned int length); + + static bool checkCRC15(const unsigned char* in, unsigned int length); + static void encodeCRC15(unsigned char* in, unsigned int length); + +private: + static uint8_t createCRC6(const unsigned char* in, unsigned int length); + static uint16_t createCRC12(const unsigned char* in, unsigned int length); + static uint16_t createCRC15(const unsigned char* in, unsigned int length); +}; + +#endif diff --git a/NXDNReflector/NXDNReflector.cpp b/NXDNReflector/NXDNReflector.cpp index 0e078a2..f8115f2 100644 --- a/NXDNReflector/NXDNReflector.cpp +++ b/NXDNReflector/NXDNReflector.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* Copyright (C) 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 @@ -16,8 +16,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "KenwoodNetwork.h" #include "NXDNReflector.h" #include "NXDNNetwork.h" +#include "IcomNetwork.h" #include "NXDNLookup.h" #include "StopWatch.h" #include "Version.h" @@ -410,6 +412,9 @@ void CNXDNReflector::run() dumpTimer.start(); } + if (m_nxCoreNetwork != NULL) + m_nxCoreNetwork->clock(ms); + if (ms < 5U) CThread::sleep(5U); } @@ -454,7 +459,11 @@ void CNXDNReflector::dumpRepeaters() const bool CNXDNReflector::openNXCore() { - m_nxCoreNetwork = new CNXCoreNetwork(m_conf.getNXCoreAddress(), m_conf.getNXCoreDebug()); + std::string protocol = m_conf.getNXCoreProtocol(); + if (protocol == "Kenwood") + m_nxCoreNetwork = new CKenwoodNetwork(m_conf.getNXCoreAddress(), m_conf.getNXCoreDebug()); + else + m_nxCoreNetwork = new CIcomNetwork(m_conf.getNXCoreAddress(), m_conf.getNXCoreDebug()); bool ret = m_nxCoreNetwork->open(); if (!ret) { delete m_nxCoreNetwork; diff --git a/NXDNReflector/NXDNReflector.h b/NXDNReflector/NXDNReflector.h index 4dcafe8..7b7ca65 100644 --- a/NXDNReflector/NXDNReflector.h +++ b/NXDNReflector/NXDNReflector.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* Copyright (C) 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 @@ -19,7 +19,7 @@ #if !defined(NXDNReflector_H) #define NXDNReflector_H -#include "NXCoreNetwork.h" +#include "CoreNetwork.h" #include "Timer.h" #include "Conf.h" @@ -65,7 +65,7 @@ public: private: CConf m_conf; - CNXCoreNetwork* m_nxCoreNetwork; + ICoreNetwork* m_nxCoreNetwork; std::vector m_repeaters; CNXDNRepeater* findRepeater(const in_addr& address, unsigned int port) const; diff --git a/NXDNReflector/NXDNReflector.ini b/NXDNReflector/NXDNReflector.ini index a59ccd5..bc0e4b6 100644 --- a/NXDNReflector/NXDNReflector.ini +++ b/NXDNReflector/NXDNReflector.ini @@ -19,6 +19,7 @@ Debug=0 [NXCore] Enabled=0 +Protocol=Icom # Address=208.111.3.45 Address=44.131.4.1 # TGEnable=1234 diff --git a/NXDNReflector/NXDNReflector.vcxproj b/NXDNReflector/NXDNReflector.vcxproj index 37b05b1..73d79ac 100644 --- a/NXDNReflector/NXDNReflector.vcxproj +++ b/NXDNReflector/NXDNReflector.vcxproj @@ -20,7 +20,10 @@ - + + + + @@ -35,7 +38,10 @@ - + + + + diff --git a/NXDNReflector/NXDNReflector.vcxproj.filters b/NXDNReflector/NXDNReflector.vcxproj.filters index e361874..4f06cff 100644 --- a/NXDNReflector/NXDNReflector.vcxproj.filters +++ b/NXDNReflector/NXDNReflector.vcxproj.filters @@ -47,7 +47,16 @@ Header Files - + + Header Files + + + Header Files + + + Header Files + + Header Files @@ -85,7 +94,16 @@ Source Files - + + Source Files + + + Source Files + + + Source Files + + Source Files diff --git a/NXDNReflector/Version.h b/NXDNReflector/Version.h index 2bd351f..301ae35 100644 --- a/NXDNReflector/Version.h +++ b/NXDNReflector/Version.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * 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 @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20180517"; +const char* VERSION = "20200420"; #endif