diff --git a/NXDNGateway/Conf.cpp b/NXDNGateway/Conf.cpp index 92cba3c..f1bebb2 100644 --- a/NXDNGateway/Conf.cpp +++ b/NXDNGateway/Conf.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,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 @@ -42,6 +42,7 @@ CConf::CConf(const std::string& file) : m_file(file), m_callsign(), m_suffix(), +m_rptProtocol("Icom"), m_rptAddress(), m_rptPort(0U), m_myPort(0U), @@ -143,7 +144,9 @@ bool CConf::read() for (unsigned int i = 0U; value[i] != 0; i++) value[i] = ::toupper(value[i]); m_suffix = value; - } else if (::strcmp(key, "RptAddress") == 0) + } else if (::strcmp(key, "RptProtocol") == 0) + m_rptProtocol = value; + else if (::strcmp(key, "RptAddress") == 0) m_rptAddress = value; else if (::strcmp(key, "RptPort") == 0) m_rptPort = (unsigned int)::atoi(value); @@ -248,6 +251,11 @@ std::string CConf::getSuffix() const return m_suffix; } +std::string CConf::getRptProtocol() const +{ + return m_rptProtocol; +} + std::string CConf::getRptAddress() const { return m_rptAddress; diff --git a/NXDNGateway/Conf.h b/NXDNGateway/Conf.h index 29dce87..b726a5a 100644 --- a/NXDNGateway/Conf.h +++ b/NXDNGateway/Conf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,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 @@ -33,6 +33,7 @@ public: // The General section std::string getCallsign() const; std::string getSuffix() const; + std::string getRptProtocol() const; std::string getRptAddress() const; unsigned int getRptPort() const; unsigned int getMyPort() const; @@ -92,6 +93,7 @@ private: std::string m_file; std::string m_callsign; std::string m_suffix; + std::string m_rptProtocol; std::string m_rptAddress; unsigned int m_rptPort; unsigned int m_myPort; diff --git a/NXDNGateway/IcomNetwork.cpp b/NXDNGateway/IcomNetwork.cpp index 056eaae..557f155 100644 --- a/NXDNGateway/IcomNetwork.cpp +++ b/NXDNGateway/IcomNetwork.cpp @@ -26,10 +26,17 @@ const unsigned int BUFFER_LENGTH = 200U; -CIcomNetwork::CIcomNetwork(unsigned int localPort, bool debug) : +CIcomNetwork::CIcomNetwork(unsigned int localPort, const std::string& rptAddress, unsigned int rptPort, bool debug) : m_socket(localPort), +m_address(), +m_port(rptPort), m_debug(debug) { + assert(localPort > 0U); + assert(!rptAddress.empty()); + assert(rptPort > 0U); + + m_address = CUDPSocket::lookup(rptAddress); } CIcomNetwork::~CIcomNetwork() @@ -46,7 +53,7 @@ bool CIcomNetwork::open() return m_socket.open(); } -bool CIcomNetwork::write(const unsigned char* data, unsigned int length, const in_addr& address, unsigned int port) +bool CIcomNetwork::write(const unsigned char* data, unsigned int length) { assert(data != NULL); @@ -77,19 +84,26 @@ bool CIcomNetwork::write(const unsigned char* data, unsigned int length, const i if (m_debug) CUtils::dump(1U, "Icom Data Sent", buffer, 102U); - return m_socket.write(buffer, 102U, address, port); + return m_socket.write(buffer, 102U, m_address, m_port); } -bool CIcomNetwork::read(unsigned char* data, in_addr& address, unsigned int& port) +bool CIcomNetwork::read(unsigned char* data) { assert(data != NULL); unsigned char buffer[BUFFER_LENGTH]; + in_addr address; + unsigned int port; int length = m_socket.read(buffer, BUFFER_LENGTH, address, port); if (length <= 0) return false; + if (m_address.s_addr != address.s_addr || m_port != port) { + LogWarning("Icom Data received from an unknown address or port - %08X:%u", ntohl(address.s_addr), port); + return false; + } + // Invalid packet type? if (::memcmp(buffer, "ICOM", 4U) != 0) return false; diff --git a/NXDNGateway/IcomNetwork.h b/NXDNGateway/IcomNetwork.h index 1c8f02d..6cb4d3a 100644 --- a/NXDNGateway/IcomNetwork.h +++ b/NXDNGateway/IcomNetwork.h @@ -21,21 +21,20 @@ #include "RptNetwork.h" #include "UDPSocket.h" -#include "Timer.h" #include #include class CIcomNetwork : public IRptNetwork { public: - CIcomNetwork(unsigned int localPort, bool debug); + CIcomNetwork(unsigned int localPort, const std::string& rptAddress, unsigned int rptPort, bool debug); virtual ~CIcomNetwork(); virtual bool open(); - virtual bool write(const unsigned char* data, unsigned int length, const in_addr& address, unsigned int port); + virtual bool write(const unsigned char* data, unsigned int length); - virtual bool read(unsigned char* data, in_addr& address, unsigned int& port); + virtual bool read(unsigned char* data); virtual void close(); diff --git a/NXDNGateway/KenwoodNetwork.cpp b/NXDNGateway/KenwoodNetwork.cpp new file mode 100644 index 0000000..1421dd8 --- /dev/null +++ b/NXDNGateway/KenwoodNetwork.cpp @@ -0,0 +1,644 @@ +/* + * 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; + +CKenwoodNetwork::CKenwoodNetwork(unsigned int localPort, const std::string& rptAddress, unsigned int rptPort, bool debug) : +m_rtcpSocket(localPort + 1U), +m_rtpSocket(localPort + 0U), +m_stopWatch(), +m_address(), +m_rtcpPort(rptPort + 1U), +m_rtpPort(rptPort + 0U), +m_seqNo(0U), +m_timeStamp(0U), +m_ssrc(0U), +m_debug(debug), +m_timer(1000U, 0U, 200U) +{ + assert(localPort > 0U); + assert(!rptAddress.empty()); + assert(rptPort > 0U); + + m_address = CUDPSocket::lookup(rptAddress); + + ::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_timer.start(); + + 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]; + + switch (outData[4U] & 0x3FU) { + case 0x01U: { + uint16_t uid = (outData[7U] << 8) + (outData[8U] << 0); + uint16_t gid = (outData[9U] << 8) + (outData[10U] << 0); + return writeRTPVoiceHeader(outData); + } + case 0x08U: + 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, m_rtpPort); +} + +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, m_rtpPort); +} + +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, m_rtpPort); +} + +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, m_rtcpPort); +} + +bool CKenwoodNetwork::writeRTCPData(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] = 0x01U; + + if (m_debug) + CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U); + + return m_rtcpSocket.write(buffer, 20U, m_address, m_rtcpPort); +} + +bool CKenwoodNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + unsigned char dummy[BUFFER_LENGTH]; + readRTCP(dummy); + + unsigned int len = readRTP(data); + + switch (data[9U]) { + case 0x05U: { // Voice header or trailer + bool ret = processKenwoodVoiceHeader(data); + if (!ret) + return false; + return true; + } + case 0x08U: // Voice data + processKenwoodVoiceData(data); + return true; + default: + CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len); + return false; + } +} + +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 != m_rtpPort) { + LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_rtpPort, 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 != m_rtcpPort) { + LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_rtcpPort, 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.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[15U]; + temp[1U] = inData[14U]; + temp[2U] = inData[17U]; + temp[3U] = inData[16U]; + temp[4U] = inData[19U]; + temp[5U] = inData[18U]; + temp[6U] = inData[21U]; + temp[7U] = inData[20U]; + temp[8U] = inData[23U]; + temp[9U] = inData[22U]; + CNXDNCRC::encodeCRC12(temp, 80U); + ::memcpy(outData + 5U, temp, 12U); + ::memcpy(outData + 19U, temp, 12U); + + switch (outData[5U] & 0x3FU) { + case 0x01U: { + unsigned short uid = (outData[8U] << 8) + (outData[9U] << 0); + unsigned short gid = (outData[10U] << 8) + (outData[11U] << 0); + ::memcpy(inData, outData, 33U); + return true; + } + 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[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); + } + + // AMBE 3+4 + n = 19U * 8U; + + 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); + } + + 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); + } + + ::memcpy(inData, outData, 33U); +} diff --git a/NXDNGateway/KenwoodNetwork.h b/NXDNGateway/KenwoodNetwork.h new file mode 100644 index 0000000..011e223 --- /dev/null +++ b/NXDNGateway/KenwoodNetwork.h @@ -0,0 +1,71 @@ +/* + * 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 "RptNetwork.h" +#include "StopWatch.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CKenwoodNetwork : public IRptNetwork { +public: + CKenwoodNetwork(unsigned int localPort, const std::string& rptAddress, unsigned int rptPort, bool debug); + virtual ~CKenwoodNetwork(); + + virtual bool open(); + + virtual bool write(const unsigned char* data, unsigned int length); + + virtual bool 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 int m_rtcpPort; + unsigned int m_rtpPort; + 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 short src, unsigned short dst); + unsigned int readRTP(unsigned char* data); + unsigned int readRTCP(unsigned char* data); +}; + +#endif diff --git a/NXDNGateway/Makefile b/NXDNGateway/Makefile index c587deb..4ec984b 100644 --- a/NXDNGateway/Makefile +++ b/NXDNGateway/Makefile @@ -4,8 +4,8 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread LIBS = -lpthread LDFLAGS = -g -OBJECTS = APRSWriter.o APRSWriterThread.o Conf.o GPSHandler.o IcomNetwork.o Log.o Mutex.o NXDNCRC.o NXDNGateway.o NXDNLookup.o NXDNNetwork.o Reflectors.o \ - RptNetwork.o StopWatch.o TCPSocket.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o +OBJECTS = APRSWriter.o APRSWriterThread.o Conf.o GPSHandler.o IcomNetwork.o KenwoodNetwork.o Log.o Mutex.o NXDNCRC.o NXDNGateway.o NXDNLookup.o NXDNNetwork.o \ + Reflectors.o RptNetwork.o StopWatch.o TCPSocket.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o all: NXDNGateway diff --git a/NXDNGateway/NXDNGateway.cpp b/NXDNGateway/NXDNGateway.cpp index 7483fb3..8f896e3 100644 --- a/NXDNGateway/NXDNGateway.cpp +++ b/NXDNGateway/NXDNGateway.cpp @@ -16,6 +16,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "KenwoodNetwork.h" #include "IcomNetwork.h" #include "NXDNNetwork.h" #include "NXDNGateway.h" @@ -114,7 +115,8 @@ void CNXDNGateway::run() if (pid == -1) { ::fprintf(stderr, "Couldn't fork() , exiting\n"); return; - } else if (pid != 0) { + } + else if (pid != 0) { exit(EXIT_SUCCESS); } @@ -175,12 +177,16 @@ void CNXDNGateway::run() } #endif - in_addr rptAddr = CUDPSocket::lookup(m_conf.getRptAddress()); - unsigned int rptPort = m_conf.getRptPort(); - createGPS(); - IRptNetwork* localNetwork = new CIcomNetwork(m_conf.getMyPort(), m_conf.getRptDebug()); + IRptNetwork* localNetwork = NULL; + std::string protocol = m_conf.getRptProtocol(); + + if (protocol == "Kenwood") + localNetwork = new CKenwoodNetwork(m_conf.getMyPort(), m_conf.getRptAddress(), m_conf.getRptPort(), m_conf.getRptDebug()); + else + localNetwork = new CIcomNetwork(m_conf.getMyPort(), m_conf.getRptAddress(), m_conf.getRptPort(), m_conf.getRptDebug()); + ret = localNetwork->open(); if (!ret) { ::LogFinalise(); @@ -274,7 +280,7 @@ startupId = 9999U; bool grp = (buffer[9U] & 0x01U) == 0x01U; if (grp && currentId == dstId) - localNetwork->write(buffer + 10U, len - 10U, rptAddr, rptPort); + localNetwork->write(buffer + 10U, len - 10U); } // Any network activity is proof that the reflector is alive @@ -283,7 +289,7 @@ startupId = 9999U; } // From the MMDVM to the reflector or control data - len = localNetwork->read(buffer, address, port); + len = localNetwork->read(buffer); if (len > 0U) { // Only process the beginning and ending voice blocks here if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && (buffer[5U] == 0x01U || buffer[5U] == 0x08U)) { @@ -383,7 +389,7 @@ startupId = 9999U; if (voice != NULL) { unsigned int length = voice->read(buffer); if (length > 0U) - localNetwork->write(buffer, length, rptAddr, rptPort); + localNetwork->write(buffer, length); } unsigned int ms = stopWatch.elapsed(); diff --git a/NXDNGateway/NXDNGateway.ini b/NXDNGateway/NXDNGateway.ini index 884d8bd..6e439d6 100644 --- a/NXDNGateway/NXDNGateway.ini +++ b/NXDNGateway/NXDNGateway.ini @@ -1,6 +1,7 @@ [General] Callsign=G4KLX Suffix=NXDN +Protocol=Icom RptAddress=127.0.0.1 RptPort=14021 LocalPort=14020 diff --git a/NXDNGateway/NXDNGateway.vcxproj b/NXDNGateway/NXDNGateway.vcxproj index 658c58c..e955110 100644 --- a/NXDNGateway/NXDNGateway.vcxproj +++ b/NXDNGateway/NXDNGateway.vcxproj @@ -151,6 +151,7 @@ + @@ -175,6 +176,7 @@ + diff --git a/NXDNGateway/NXDNGateway.vcxproj.filters b/NXDNGateway/NXDNGateway.vcxproj.filters index bc39c26..4215f7e 100644 --- a/NXDNGateway/NXDNGateway.vcxproj.filters +++ b/NXDNGateway/NXDNGateway.vcxproj.filters @@ -77,6 +77,9 @@ Header Files + + Header Files + @@ -139,5 +142,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/NXDNGateway/RptNetwork.h b/NXDNGateway/RptNetwork.h index 36ec99b..0ec1a06 100644 --- a/NXDNGateway/RptNetwork.h +++ b/NXDNGateway/RptNetwork.h @@ -41,9 +41,9 @@ public: virtual bool open() = 0; - virtual bool write(const unsigned char* data, unsigned int length, const in_addr& address, unsigned int port) = 0; + virtual bool write(const unsigned char* data, unsigned int length) = 0; - virtual bool read(unsigned char* data, in_addr& address, unsigned int& port) = 0; + virtual bool read(unsigned char* data) = 0; virtual void close() = 0; diff --git a/NXDNGateway/Version.h b/NXDNGateway/Version.h index 53badbe..b6d79f8 100644 --- a/NXDNGateway/Version.h +++ b/NXDNGateway/Version.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,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 = "20180524"; +const char* VERSION = "20200406"; #endif