Merge pull request #1 from g4klx/master

merging to head
This commit is contained in:
ku-mi 2020-06-11 20:27:48 +02:00 committed by GitHub
commit 8bacc79356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 3092 additions and 162 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2014,2016,2017,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2010-2014,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
@ -112,6 +112,8 @@ void CAPRSWriter::clock(unsigned int ms)
{
m_idTimer.clock(ms);
m_thread->clock(ms);
if (m_socket != NULL) {
if (m_idTimer.hasExpired()) {
pollGPS();

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2014,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2010-2014,2016,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
@ -41,6 +41,8 @@ m_socket(address, port),
m_queue(20U, "APRS Queue"),
m_exit(false),
m_connected(false),
m_reconnectTimer(1000U),
m_tries(1U),
m_APRSReadCallback(NULL),
m_filter(),
m_clientName("YSFGateway")
@ -63,6 +65,8 @@ m_socket(address, port),
m_queue(20U, "APRS Queue"),
m_exit(false),
m_connected(false),
m_reconnectTimer(1000U),
m_tries(1U),
m_APRSReadCallback(NULL),
m_filter(filter),
m_clientName(clientName)
@ -94,19 +98,28 @@ void CAPRSWriterThread::entry()
LogMessage("Starting the APRS Writer thread");
m_connected = connect();
if (!m_connected) {
LogError("Connect attempt to the APRS server has failed");
startReconnectionTimer();
}
try {
while (!m_exit) {
if (!m_connected) {
m_connected = connect();
if (m_reconnectTimer.isRunning() && m_reconnectTimer.hasExpired()) {
m_reconnectTimer.stop();
if (!m_connected){
LogError("Reconnect attempt to the APRS server has failed");
sleep(10000UL); // 10 secs
m_connected = connect();
if (!m_connected) {
LogError("Reconnect attempt to the APRS server has failed");
startReconnectionTimer();
}
}
}
if (m_connected) {
m_tries = 0U;
if (!m_queue.isEmpty()){
char* p = NULL;
m_queue.getData(&p, 1U);
@ -115,11 +128,12 @@ void CAPRSWriterThread::entry()
::strcat(p, "\r\n");
bool ret = m_socket.write((unsigned char*)p, ::strlen(p));
bool ret = m_socket.write((unsigned char*)p, (unsigned int)::strlen(p));
if (!ret) {
m_connected = false;
m_socket.close();
LogError("Connection to the APRS thread has failed");
startReconnectionTimer();
}
delete[] p;
@ -132,6 +146,7 @@ void CAPRSWriterThread::entry()
m_connected = false;
m_socket.close();
LogError("Error when reading from the APRS server");
startReconnectionTimer();
}
if(length > 0 && line.at(0U) != '#'//check if we have something and if that something is an APRS frame
@ -176,7 +191,7 @@ void CAPRSWriterThread::write(const char* data)
if (!m_connected)
return;
unsigned int len = ::strlen(data);
unsigned int len = (unsigned int)::strlen(data);
char* p = new char[len + 5U];
::strcpy(p, data);
@ -196,6 +211,11 @@ void CAPRSWriterThread::stop()
wait();
}
void CAPRSWriterThread::clock(unsigned int ms)
{
m_reconnectTimer.clock(ms);
}
bool CAPRSWriterThread::connect()
{
bool ret = m_socket.open();
@ -246,3 +266,14 @@ bool CAPRSWriterThread::connect()
return true;
}
void CAPRSWriterThread::startReconnectionTimer()
{
// Clamp at a ten minutes reconnect time
m_tries++;
if (m_tries > 10U)
m_tries = 10U;
m_reconnectTimer.setTimeout(m_tries * 60U);
m_reconnectTimer.start();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010,2011,2012,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2010,2011,2012,2016,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
@ -21,6 +21,7 @@
#include "TCPSocket.h"
#include "RingBuffer.h"
#include "Timer.h"
#include "Thread.h"
#include <string>
@ -45,6 +46,8 @@ public:
void setReadAPRSCallback(ReadAPRSFrameCallback cb);
void clock(unsigned int ms);
private:
std::string m_username;
std::string m_password;
@ -52,11 +55,14 @@ private:
CRingBuffer<char*> m_queue;
bool m_exit;
bool m_connected;
CTimer m_reconnectTimer;
unsigned int m_tries;
ReadAPRSFrameCallback m_APRSReadCallback;
std::string m_filter;
std::string m_clientName;
bool connect();
void startReconnectionTimer();
};
#endif

View File

@ -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
@ -35,13 +35,15 @@ enum SECTION {
SECTION_LOG,
SECTION_APRS_FI,
SECTION_NETWORK,
SECTION_MOBILE_GPS
SECTION_MOBILE_GPS,
SECTION_REMOTE_COMMANDS
};
CConf::CConf(const std::string& file) :
m_file(file),
m_callsign(),
m_suffix(),
m_rptProtocol("Icom"),
m_rptAddress(),
m_rptPort(0U),
m_myPort(0U),
@ -81,7 +83,9 @@ m_networkInactivityTimeout(0U),
m_networkDebug(false),
m_mobileGPSEnabled(false),
m_mobileGPSAddress(),
m_mobileGPSPort(0U)
m_mobileGPSPort(0U),
m_remoteCommandsEnabled(false),
m_remoteCommandsPort(6075U)
{
}
@ -121,6 +125,8 @@ bool CConf::read()
section = SECTION_NETWORK;
else if (::strncmp(buffer, "[Mobile GPS]", 12U) == 0)
section = SECTION_MOBILE_GPS;
else if (::strncmp(buffer, "[Remote Commands]", 17U) == 0)
section = SECTION_REMOTE_COMMANDS;
else
section = SECTION_NONE;
@ -143,7 +149,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);
@ -230,6 +238,11 @@ bool CConf::read()
m_mobileGPSAddress = value;
else if (::strcmp(key, "Port") == 0)
m_mobileGPSPort = (unsigned int)::atoi(value);
} else if (section == SECTION_REMOTE_COMMANDS) {
if (::strcmp(key, "Enable") == 0)
m_remoteCommandsEnabled = ::atoi(value) == 1;
else if (::strcmp(key, "Port") == 0)
m_remoteCommandsPort = (unsigned int)::atoi(value);
}
}
@ -248,6 +261,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;
@ -448,3 +466,12 @@ unsigned int CConf::getMobileGPSPort() const
return m_mobileGPSPort;
}
bool CConf::getRemoteCommandsEnabled() const
{
return m_remoteCommandsEnabled;
}
unsigned int CConf::getRemoteCommandsPort() const
{
return m_remoteCommandsPort;
}

View File

@ -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;
@ -84,14 +85,19 @@ public:
bool getNetworkDebug() const;
// The Mobile GPS section
bool getMobileGPSEnabled() const;
std::string getMobileGPSAddress() const;
unsigned int getMobileGPSPort() const;
bool getMobileGPSEnabled() const;
std::string getMobileGPSAddress() const;
unsigned int getMobileGPSPort() const;
// The Remote Commands section
bool getRemoteCommandsEnabled() const;
unsigned int getRemoteCommandsPort() const;
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;
@ -139,6 +145,9 @@ private:
bool m_mobileGPSEnabled;
std::string m_mobileGPSAddress;
unsigned int m_mobileGPSPort;
bool m_remoteCommandsEnabled;
unsigned int m_remoteCommandsPort;
};
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 by Jonathan Naylor G4KLX
* Copyright (C) 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
@ -63,8 +63,12 @@ void CGPSHandler::processData(const unsigned char* data)
::memcpy(m_data + m_length, data + 1U, NXDN_DATA_LENGTH);
m_length += NXDN_DATA_LENGTH;
// The packet frame number and block number are zero to indicate the last section of the
// data transmission.
if (data[0U] == 0x00U) {
processNMEA();
bool ret = processIcom();
if (!ret)
processKenwood();
reset();
}
}
@ -81,23 +85,25 @@ void CGPSHandler::reset()
m_source.clear();
}
void CGPSHandler::processNMEA()
bool CGPSHandler::processIcom()
{
if (m_data[0U] != NXDN_DATA_TYPE_GPS)
return;
return false;
if (::memcmp(m_data + 1U, "$G", 2U) != 0)
return;
return false;
if (::strchr((char*)(m_data + 1U), '*') == NULL)
return;
return false;
// From here onwards we have something that looks like Icom GPS data
if (!checkXOR())
return;
return true;
if (::memcmp(m_data + 4U, "RMC", 3U) != 0) {
CUtils::dump("Unhandled NMEA sentence", (unsigned char*)(m_data + 1U), m_length - 1U);
return;
return true;
}
// Parse the $GxRMC string into tokens
@ -114,11 +120,11 @@ void CGPSHandler::processNMEA()
// Is there any position data?
if (pRMC[3U] == NULL || pRMC[4U] == NULL || pRMC[5U] == NULL || pRMC[6U] == NULL || ::strlen(pRMC[3U]) == 0U || ::strlen(pRMC[4U]) == 0U || ::strlen(pRMC[5U]) == 0 || ::strlen(pRMC[6U]) == 0)
return;
return true;
// Is it a valid GPS fix?
if (::strcmp(pRMC[2U], "A") != 0)
return;
return true;
double latitude = ::atof(pRMC[3U]);
double longitude = ::atof(pRMC[5U]);
@ -134,14 +140,16 @@ void CGPSHandler::processNMEA()
int bearing = ::atoi(pRMC[8U]);
int speed = ::atoi(pRMC[7U]);
::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%07.2lf%s/%08.2lf%sr%03d/%03d via MMDVM",
::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%07.2lf%s/%08.2lf%sr%03d/%03d Icom IDAS via MMDVM",
source.c_str(), m_callsign.c_str(), latitude, pRMC[4U], longitude, pRMC[6U], bearing, speed);
} else {
::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%07.2lf%s/%08.2lf%sr via MMDVM",
::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%07.2lf%s/%08.2lf%sr Icom IDAS via MMDVM",
source.c_str(), m_callsign.c_str(), latitude, pRMC[4U], longitude, pRMC[6U]);
}
m_writer->write(output);
return true;
}
bool CGPSHandler::checkXOR() const
@ -158,3 +166,87 @@ bool CGPSHandler::checkXOR() const
return ::memcmp(buffer, p2 + 1U, 2U) == 0;
}
bool CGPSHandler::processKenwood()
{
enum {
GPS_FULL,
GPS_SHORT,
GPS_VERY_SHORT
} type;
switch (m_data[0U]) {
case 0x00U:
if (m_length < 38U)
return false;
type = GPS_FULL;
break;
case 0x01U:
if (m_length < 17U)
return false;
type = GPS_SHORT;
break;
case 0x02U:
if (m_length < 15U)
return false;
type = GPS_VERY_SHORT;
break;
default:
return true;
}
unsigned char north = 'N';
unsigned int latAfter = 0x7FFFU;
unsigned int latBefore = 0xFFFFU;
unsigned char east = 'E';
unsigned int lonAfter = 0x7FFFU;
unsigned int lonBefore = 0xFFFFU;
if (type == GPS_VERY_SHORT) {
north = (m_data[7U] & 0x01U) == 0x00U ? 'N' : 'S';
latAfter = ((m_data[7U] & 0xFEU) >> 1) | (m_data[8U] << 7);
latBefore = (m_data[10U] << 8) | m_data[9U];
east = (m_data[11U] & 0x01U) == 0x00U ? 'E' : 'W';
lonAfter = ((m_data[11U] & 0xFEU) >> 1) | (m_data[12U] << 7);
lonBefore = (m_data[13U] << 8) | m_data[13U];
} else {
north = (m_data[9U] & 0x01U) == 0x00U ? 'N' : 'S';
latAfter = ((m_data[9U] & 0xFEU) >> 1) | (m_data[10U] << 7);
latBefore = (m_data[12U] << 8) | m_data[11U];
east = (m_data[13U] & 0x01U) == 0x00U ? 'E' : 'W';
lonAfter = ((m_data[13U] & 0xFEU) >> 1) | (m_data[14U] << 7);
lonBefore = (m_data[16U] << 8) | m_data[15U];
}
if (latAfter == 0x7FFFU || latBefore == 0xFFFFU || lonAfter == 0x7FFFU || lonBefore == 0xFFFFU)
return true;
unsigned int course = 0xFFFFU;
unsigned int speedBefore = 0x3FFU;
unsigned int speedAfter = 0x0FU;
if (type == GPS_FULL) {
course = (m_data[23U] << 4) | (m_data[24U] & 0x0FU);
speedBefore = ((m_data[25U] & 0xF0U) << 2) | (m_data[26U] & 0x3FU);
speedAfter = m_data[25U] & 0x0FU;
}
std::string source = m_source;
if (!m_suffix.empty()) {
source.append("-");
source.append(m_suffix.substr(0U, 1U));
}
char output[300U];
if (course != 0xFFFFU && speedBefore != 0x3FFU && speedAfter != 0x0FU) {
::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%04u.%02u%c/%05u.%02u%cr%03d/%03d Kenwood NEXEDGE via MMDVM",
source.c_str(), m_callsign.c_str(), latBefore, latAfter / 100U, north, lonBefore, lonAfter / 100U, east, course / 10U, speedBefore);
} else {
::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%04u.%02u%c/%05u.%02u%cr Kenwood NEXEDGE via MMDVM",
source.c_str(), m_callsign.c_str(), latBefore, latAfter / 100U, north, lonBefore, lonAfter / 100U, east);
}
m_writer->write(output);
return true;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 by Jonathan Naylor G4KLX
* Copyright (C) 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,7 +42,8 @@ private:
std::string m_source;
std::string m_suffix;
void processNMEA();
bool processIcom();
bool processKenwood();
bool checkXOR() const;
void reset();
};

View File

@ -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
@ -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,22 +84,29 @@ 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)
unsigned int 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;
return 0U;
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 0U;
}
// Invalid packet type?
if (::memcmp(buffer, "ICOM", 4U) != 0)
return false;
return 0U;
// An Icom repeater connect request
if (buffer[4U] == 0x01U && buffer[5U] == 0x61U) {
@ -101,18 +115,18 @@ bool CIcomNetwork::read(unsigned char* data, in_addr& address, unsigned int& por
buffer[38U] = 0x4FU;
buffer[39U] = 0x4BU;
m_socket.write(buffer, length, address, port);
return false;
return 0U;
}
if (length != 102)
return false;
return 0U;
if (m_debug)
CUtils::dump(1U, "Icom Data Received", buffer, length);
::memcpy(data, buffer + 40U, 33U);
return true;
return 33U;
}
void CIcomNetwork::close()
@ -121,3 +135,7 @@ void CIcomNetwork::close()
LogMessage("Closing Icom connection");
}
void CIcomNetwork::clock(unsigned int ms)
{
}

View File

@ -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
@ -19,24 +19,26 @@
#ifndef IcomNetwork_H
#define IcomNetwork_H
#include "RptNetwork.h"
#include "UDPSocket.h"
#include "Timer.h"
#include <cstdint>
#include <string>
class CIcomNetwork {
class CIcomNetwork : public IRptNetwork {
public:
CIcomNetwork(unsigned int localPort, bool debug);
~CIcomNetwork();
CIcomNetwork(unsigned int localPort, const std::string& rptAddress, unsigned int rptPort, bool debug);
virtual ~CIcomNetwork();
bool open();
virtual bool open();
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);
bool read(unsigned char* data, in_addr& address, unsigned int& port);
virtual unsigned int read(unsigned char* data);
void close();
virtual void close();
virtual void clock(unsigned int ms);
private:
CUDPSocket m_socket;

View File

@ -0,0 +1,924 @@
/*
* 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 <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;
CKenwoodNetwork::CKenwoodNetwork(unsigned int localPort, const std::string& rptAddress, unsigned int rptPort, bool debug) :
m_rtpSocket(localPort + 0U),
m_rtcpSocket(localPort + 1U),
m_address(),
m_rtcpPort(rptPort + 1U),
m_rtpPort(rptPort + 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)
{
assert(localPort > 0U);
assert(!rptAddress.empty());
assert(rptPort > 0U);
m_sacch = new unsigned char[10U];
m_address = CUDPSocket::lookup(rptAddress);
}
CKenwoodNetwork::~CKenwoodNetwork()
{
delete[] m_sacch;
}
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 = m_rtpSocket.getLocalAddress();
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_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;
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_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;
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_address, m_rtpPort);
}
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_address, m_rtcpPort);
}
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_address, m_rtcpPort);
}
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_address, m_rtcpPort);
}
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];
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) {
LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
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];
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) {
LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
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[25U];
outData[21U] = inData[28U];
outData[22U] = inData[27U];
outData[23U] = inData[29U];
::memcpy(inData, outData, 24U);
return 24U;
}
}
unsigned long CKenwoodNetwork::getTimeStamp() const
{
unsigned long timeStamp = 0UL;
#if defined(_WIN32) || defined(_WIN64)
SYSTEMTIME st;
::GetSystemTime(&st);
unsigned int hh = st.wHour;
unsigned int mm = st.wMinute;
unsigned int ss = st.wSecond;
unsigned int ms = st.wMilliseconds;
timeStamp += hh * 3600U * 1000U * 80U;
timeStamp += mm * 60U * 1000U * 80U;
timeStamp += ss * 1000U * 80U;
timeStamp += ms * 80U;
#else
struct timeval tod;
::gettimeofday(&tod, NULL);
unsigned int ss = tod.tv_sec;
unsigned int ms = tod.tv_usec / 1000U;
timeStamp += ss * 1000U * 80U;
timeStamp += ms * 80U;
#endif
return timeStamp;
}
unsigned int CKenwoodNetwork::processKenwoodVoiceLateEntry(unsigned char* inData)
{
assert(inData != NULL);
unsigned char sacch[4U];
sacch[0U] = inData[12U];
sacch[1U] = inData[11U];
sacch[2U] = inData[14U];
sacch[3U] = inData[13U];
switch (sacch[0U] & 0xC0U) {
case 0xC0U:
if (!m_seen1) {
unsigned int offset = 0U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen1 = true;
}
break;
case 0x80U:
if (!m_seen2) {
unsigned int offset = 18U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen2 = true;
}
break;
case 0x40U:
if (!m_seen3) {
unsigned int offset = 36U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen3 = true;
}
break;
case 0x00U:
if (!m_seen4) {
unsigned int offset = 54U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen4 = true;
}
break;
}
if (!m_seen1 || !m_seen2 || !m_seen3 || !m_seen4)
return 0U;
// Create a dummy header
// Header SACCH
inData[11U] = 0x10U;
inData[12U] = 0x01U;
inData[13U] = 0x00U;
inData[14U] = 0x00U;
// Header FACCH
inData[15U] = m_sacch[1U];
inData[16U] = m_sacch[0U];
inData[17U] = m_sacch[3U];
inData[18U] = m_sacch[2U];
inData[19U] = m_sacch[5U];
inData[20U] = m_sacch[4U];
inData[21U] = m_sacch[7U];
inData[22U] = m_sacch[6U];
inData[23U] = 0x00U;
inData[24U] = m_sacch[8U];
return processKenwoodVoiceHeader(inData);
}

View File

@ -0,0 +1,86 @@
/*
* 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 "UDPSocket.h"
#include "Timer.h"
#include <cstdint>
#include <string>
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 unsigned int read(unsigned char* data);
virtual void close();
virtual void clock(unsigned int ms);
private:
CUDPSocket m_rtpSocket;
CUDPSocket m_rtcpSocket;
in_addr m_address;
unsigned int m_rtcpPort;
unsigned int m_rtpPort;
bool m_headerSeen;
bool m_seen1;
bool m_seen2;
bool m_seen3;
bool m_seen4;
unsigned char* m_sacch;
uint8_t m_sessionId;
uint16_t m_seqNo;
unsigned int m_ssrc;
bool m_debug;
uint32_t m_startSecs;
uint32_t m_startUSecs;
CTimer m_rtcpTimer;
CTimer m_hangTimer;
unsigned char m_hangType;
unsigned short m_hangSrc;
unsigned short m_hangDst;
bool processIcomVoiceHeader(const unsigned char* data);
bool processIcomVoiceData(const unsigned char* data);
unsigned int processKenwoodVoiceHeader(unsigned char* data);
unsigned int processKenwoodVoiceData(unsigned char* data);
unsigned int processKenwoodVoiceLateEntry(unsigned char* data);
unsigned int processKenwoodData(unsigned char* data);
bool writeRTPVoiceHeader(const unsigned char* data);
bool writeRTPVoiceData(const unsigned char* data);
bool writeRTPVoiceTrailer(const unsigned char* data);
bool writeRTCPStart();
bool writeRTCPPing();
bool writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst);
bool writeRTCPHang();
unsigned int readRTP(unsigned char* data);
unsigned int readRTCP(unsigned char* data);
unsigned long getTimeStamp() const;
};
#endif

View File

@ -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 \
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

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX
* Copyright (C) 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
@ -16,9 +16,11 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "KenwoodNetwork.h"
#include "IcomNetwork.h"
#include "NXDNNetwork.h"
#include "NXDNGateway.h"
#include "RptNetwork.h"
#include "NXDNLookup.h"
#include "Reflectors.h"
#include "GPSHandler.h"
@ -27,6 +29,7 @@
#include "Thread.h"
#include "Voice.h"
#include "Timer.h"
#include "Utils.h"
#include "Log.h"
#if defined(_WIN32) || defined(_WIN64)
@ -113,7 +116,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);
}
@ -174,13 +178,17 @@ void CNXDNGateway::run()
}
#endif
in_addr rptAddr = CUDPSocket::lookup(m_conf.getRptAddress());
unsigned int rptPort = m_conf.getRptPort();
createGPS();
CIcomNetwork localNetwork(m_conf.getMyPort(), m_conf.getRptDebug());
ret = localNetwork.open();
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();
return;
@ -189,11 +197,22 @@ void CNXDNGateway::run()
CNXDNNetwork remoteNetwork(m_conf.getNetworkPort(), m_conf.getCallsign(), m_conf.getNetworkDebug());
ret = remoteNetwork.open();
if (!ret) {
localNetwork.close();
localNetwork->close();
delete localNetwork;
::LogFinalise();
return;
}
CUDPSocket* remoteSocket = NULL;
if (m_conf.getRemoteCommandsEnabled()) {
remoteSocket = new CUDPSocket(m_conf.getRemoteCommandsPort());
ret = remoteSocket->open();
if (!ret) {
delete remoteSocket;
remoteSocket = NULL;
}
}
CReflectors reflectors(m_conf.getNetworkHosts1(), m_conf.getNetworkHosts2(), m_conf.getNetworkReloadTime());
if (m_conf.getNetworkParrotPort() > 0U)
reflectors.setParrot(m_conf.getNetworkParrotAddress(), m_conf.getNetworkParrotPort());
@ -249,7 +268,7 @@ void CNXDNGateway::run()
LogMessage("Linked at startup to reflector %u", currentId);
} else {
startupId = 9999U;
startupId = 9999U;
}
}
@ -272,7 +291,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
@ -281,7 +300,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)) {
@ -381,7 +400,80 @@ startupId = 9999U;
if (voice != NULL) {
unsigned int length = voice->read(buffer);
if (length > 0U)
localNetwork.write(buffer, length, rptAddr, rptPort);
localNetwork->write(buffer, length);
}
if (remoteSocket != NULL) {
int res = remoteSocket->read(buffer, 200U, address, port);
if (res > 0) {
buffer[res] = '\0';
if (::memcmp(buffer + 0U, "TalkGroup", 9U) == 0) {
unsigned int tg = (unsigned int)::atoi((char*)(buffer + 9U));
CNXDNReflector* reflector = NULL;
if (tg != 9999U)
reflector = reflectors.find(tg);
if (reflector == NULL && currentId != 9999U) {
LogMessage("Unlinked from reflector %u by remote command", currentId);
if (voice != NULL)
voice->unlinked();
remoteNetwork.writeUnlink(currentAddr, currentPort, currentId);
remoteNetwork.writeUnlink(currentAddr, currentPort, currentId);
remoteNetwork.writeUnlink(currentAddr, currentPort, currentId);
inactivityTimer.stop();
pollTimer.stop();
lostTimer.stop();
currentId = 9999U;
} else if (reflector != NULL && currentId == 9999U) {
currentId = tg;
currentAddr = reflector->m_address;
currentPort = reflector->m_port;
LogMessage("Linked to reflector %u by remote command", currentId);
if (voice != NULL)
voice->linkedTo(currentId);
remoteNetwork.writePoll(currentAddr, currentPort, currentId);
remoteNetwork.writePoll(currentAddr, currentPort, currentId);
remoteNetwork.writePoll(currentAddr, currentPort, currentId);
inactivityTimer.start();
pollTimer.start();
lostTimer.start();
} else if (reflector != NULL && currentId != 9999U) {
LogMessage("Unlinked from reflector %u by remote command", currentId);
remoteNetwork.writeUnlink(currentAddr, currentPort, currentId);
remoteNetwork.writeUnlink(currentAddr, currentPort, currentId);
remoteNetwork.writeUnlink(currentAddr, currentPort, currentId);
currentId = tg;
currentAddr = reflector->m_address;
currentPort = reflector->m_port;
LogMessage("Linked to reflector %u by remote command", currentId);
if (voice != NULL)
voice->linkedTo(currentId);
remoteNetwork.writePoll(currentAddr, currentPort, currentId);
remoteNetwork.writePoll(currentAddr, currentPort, currentId);
remoteNetwork.writePoll(currentAddr, currentPort, currentId);
inactivityTimer.start();
pollTimer.start();
lostTimer.start();
}
} else {
CUtils::dump("Invalid remote command received", buffer, res);
}
}
}
unsigned int ms = stopWatch.elapsed();
@ -389,6 +481,8 @@ startupId = 9999U;
reflectors.clock(ms);
localNetwork->clock(ms);
if (voice != NULL)
voice->clock(ms);
@ -469,7 +563,13 @@ startupId = 9999U;
delete voice;
localNetwork.close();
localNetwork->close();
delete localNetwork;
if (remoteSocket != NULL) {
remoteSocket->close();
delete remoteSocket;
}
remoteNetwork.close();

View File

@ -1,6 +1,18 @@
[General]
Callsign=G4KLX
Suffix=NXDN
# The next four lines are for a Kenwood repeater
# RptProtocol=Kenwood
# RptAddress=1.2.3.4
# RptPort=64000
# LocalPort=64000
# The next four lines are for an Icom repeater
# RptProtocol=Icom
# RptAddress=1.2.3.4
# RptPort=41300
# LocalPort=41300
# The next four lines are for an MMDVM
RptProtocol=Icom
RptAddress=127.0.0.1
RptPort=14021
LocalPort=14020
@ -57,3 +69,6 @@ Enable=0
Address=127.0.0.1
Port=7834
[Remote Commands]
Enable=0
Port=6075

View File

@ -22,32 +22,32 @@
<ProjectGuid>{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>NXDNGateway</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
@ -151,6 +151,7 @@
<ClInclude Include="Conf.h" />
<ClInclude Include="GPSHandler.h" />
<ClInclude Include="IcomNetwork.h" />
<ClInclude Include="KenwoodNetwork.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="Mutex.h" />
<ClInclude Include="NXDNCRC.h" />
@ -159,6 +160,7 @@
<ClInclude Include="NXDNNetwork.h" />
<ClInclude Include="Reflectors.h" />
<ClInclude Include="RingBuffer.h" />
<ClInclude Include="RptNetwork.h" />
<ClInclude Include="StopWatch.h" />
<ClInclude Include="TCPSocket.h" />
<ClInclude Include="Thread.h" />
@ -174,6 +176,7 @@
<ClCompile Include="Conf.cpp" />
<ClCompile Include="GPSHandler.cpp" />
<ClCompile Include="IcomNetwork.cpp" />
<ClCompile Include="KenwoodNetwork.cpp" />
<ClCompile Include="Log.cpp" />
<ClCompile Include="Mutex.cpp" />
<ClCompile Include="NXDNCRC.cpp" />
@ -181,6 +184,7 @@
<ClCompile Include="NXDNLookup.cpp" />
<ClCompile Include="NXDNNetwork.cpp" />
<ClCompile Include="Reflectors.cpp" />
<ClCompile Include="RptNetwork.cpp" />
<ClCompile Include="StopWatch.cpp" />
<ClCompile Include="TCPSocket.cpp" />
<ClCompile Include="Thread.cpp" />

View File

@ -74,6 +74,12 @@
<ClInclude Include="GPSHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RptNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KenwoodNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Conf.cpp">
@ -133,5 +139,11 @@
<ClCompile Include="GPSHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RptNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KenwoodNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -2,29 +2,70 @@
#
# The format of this file is the number of the Talk Group followed by the host name or address and port
#
# HELLAS Zone TG202
202 hellaszone.com 41400
# RED NXDN SPAIN
214 nxdn214.xreflector.es 41400
# HBLink Poland ,TG260
260 nxdn.hblink.pl 41400
# 302, P25 Canada
302 p25canada.hopto.org 41400
# Super Freq 420
420 hb.superfreqdigital.com 41400
# VKCore, 505
505 43.229.63.42 41450
# New Zealand, 530
530 101.98.22.194 41400
530 zldigitalreflectors.hopto.org 41400
# XLX545E PA NXDN
545 70.44.20.24 41401
# RuralMN Reflector 707
707 707nxdn.kd0ioe.com 41400
# 911 Cop Talk, Multimode TG for Police and First Responders
911 nxdn.k2cop.com 41400
# 914 Latin America
914 164.132.96.157 41400
# Florida, 1200
1200 florida.nxref.org 41400
# 2140 DMRplus BM Multi
2140 94.177.235.81 41400
# 2225 IT Tuscany
2225 80.211.99.134 41400
# 2249 IT SICILIA
2249 nxdn.digitalsicilia.it 41400
# RU DMR TG2503
2503 nxdn.r1ik.ru 41400
# 3023 Ontario Crosslink
3023 ontxlink.hopto.org 41400
# 3142 Pennsylvania
3142 3.215.215.169 41402
# VK7 TAS, 5057
5057 45.248.50.37 41400
# BM NXCore bridge, 50599
50599 nxdn.duckdns.org 41490
# Multiprotocolo Argentina
7225 ysfarg.ddns.net 41400
# North America, 10200
10200 dvswitch.org 42400
# Portuguese speaking test, 10268
26810 84.90.7.110 41400
# Spanish speaking, 10301
# HBLINK Espana, 10301
10301 ea5gvk.duckdns.org 41400
# NXDN 10302 Multimode BM 21461 EA Spain
@ -42,8 +83,31 @@
# Europe, German speaking, 20000
20000 89.185.97.38 41400
# CT NXCore, 25000
25000 45.62.211.216 41425
# 20945 Deutschland DL1BH
20945 dl1bh.ddns.net 41400
# 21465 ADER Multimode
21465 80.211.106.186 41400
# 22200 IT HBLINK ITALY
22200 nxdn.hblink.it 41400
# 22202 IT NXDN Sardegna
22202 nxdn.is0hha.hblink.it 41400
# 22220 IT ECHOLINK ITALY
22220 dagobah.hblink.it 41400
# 22245 IT PIEDMONT GDO
22245 nxdngdo.duckdns.org 41400
# 22825 Swiss NXDN Reflerctor
22825 212.237.33.114 41400
# NXDN Scotland, 23551
23551 nxdnscotland.ddns.net 41400
# NX Core, 25000
25000 173.166.94.77 41400
# Russia NXDN Net, 25641
25641 194.182.85.217 41400
@ -51,6 +115,12 @@
# Poland, 26000
26000 31.0.161.238 41400
# Deutschland, 26200
26200 26200.ham-nxdn.de 41400
# Portuguese speaking test, 10268
26810 84.90.7.110 41400
# America-Ragchew, 28299
28299 65.101.7.52 41400
@ -60,24 +130,39 @@
# Alabama-Link, 31010
31010 nxdn.alabamalink.info 41400
# Colorado Fun Machine WE0FUN Bridge to C4FM, DMR, DStar, P25 and AllStarLink (Analog) http://www.we0fun.com
31081 nxdn.we0fun.com 41400
# Colorado HD, 31088
31088 54.191.50.212 41400
# Connecticut Chat, 31092
31092 nxdn.alecwasserman.com 41400
# Kingsland Digital Link
31137 74.91.119.94 41400
# Illinois, 31171
31171 74.208.235.115 41400
# Southern Indiana, 31188
31188 w9windigital.org 41400
# 31264 XLX625 The BROniverse www.wa8bro.com
31264 nxdn.dudetronics.com 41400
# Central New Jersey, 31340
31340 cnjham.msmts.com 41400
# Oklahoma Link, 31403
31403 3.208.70.29 41400
# XLX545A Pennsylvania Cross Mode, 31425
31425 70.44.20.24 41400
# XLX545A Pennsylvania Cross Mode (alt), 31426
31426 3.215.215.169 41400
# Rhode Island Digital Link, 31444
31444 18.219.32.21 41400
@ -88,7 +173,16 @@
31672 nxdn-31672.pistar.uk 41400
# DX-LINK SYSTEM, 31777
31777 45.77.204.214 41400
31777 8.9.4.102 41400
# K8JTK Hub Multimode ILS/DVMIS (K8JTK.org), 31983
31983 NXDNReflector31983.K8JTK.org 41400
# CW-Ops Academy, NXDN Reflector 32103
32103 cwops.dyndns.org 41400
# OMISS Group, NXDN Reflector 33581
33581 omiss.dyndns.org 41400
# Fusion Canada Fr /Wires-x /Ysf /Nxdn Network
40721 38.110.97.161 41400
@ -99,6 +193,9 @@
# DMR TG50525 bridge, 50525
50525 50525.nxref.org 41400
# BM NXCore bridge, 50599
50599 nxdn.duckdns.org 41490
# Thailand DTDXA XLX520N, 52000
52000 nxdn.dtdxa.com 41400
@ -106,22 +203,13 @@
53099 203.86.206.49 41400
# World Wide, 65000
65000 176.9.1.168 41400
65000 176.9.1.168 41400
# French-Test, 65208
65208 m55.evxonline.net 41400
# 3023 Ontario Crosslink
3023 ontxlink.hopto.org 41400
# NXDN - 2007DXGROUP
65100 89.46.75.115 41400
# 2140 DMRplus BM Multi
2140 94.177.235.81 41400
# 2225 IT Tuscany
2225 80.211.99.134 41400
# 21465 ADER Multimode
21465 80.211.106.186 41400
# 22245 IT PIEDMONT GDO
22245 nxdngdo.duckdns.org 41400
# NXDN SPAIN 21410
21410 nxdn21410.nxdn.es 41400

View File

@ -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 "RptNetwork.h"
IRptNetwork::~IRptNetwork()
{
}

55
NXDNGateway/RptNetwork.h Normal file
View File

@ -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 RptNetwork_H
#define RptNetwork_H
#if !defined(_WIN32) && !defined(_WIN64)
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#else
#include <winsock.h>
#endif
#include <cstdint>
#include <string>
class IRptNetwork {
public:
virtual ~IRptNetwork() = 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

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2016 by Jonathan Naylor G4KLX
* Copyright (C) 2006-2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,6 +24,7 @@
#if !defined(_WIN32) && !defined(_WIN64)
#include <cerrno>
#include <cstring>
#include <ifaddrs.h>
#endif
@ -260,3 +261,31 @@ void CUDPSocket::close()
::close(m_fd);
#endif
}
unsigned long CUDPSocket::getLocalAddress() const
{
unsigned long address = 0UL;
char hostname[80U];
int ret = ::gethostname(hostname, 80);
if (ret == -1)
return 0UL;
struct hostent* phe = ::gethostbyname(hostname);
if (phe == NULL)
return 0UL;
if (phe->h_addrtype != AF_INET)
return 0UL;
for (unsigned int i = 0U; phe->h_addr_list[i] != NULL; i++) {
struct in_addr addr;
::memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
if (addr.s_addr != INADDR_LOOPBACK) {
address = addr.s_addr;
break;
}
}
return address;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2011,2013,2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -47,6 +47,8 @@ public:
void close();
unsigned long getLocalAddress() const;
static in_addr lookup(const std::string& hostName);
private:

View File

@ -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 = "20200519";
#endif

View File

@ -22,32 +22,32 @@
<ProjectGuid>{2AE94EAA-FD57-45C9-8555-6425CFA777A3}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>NXDNParrot</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@ -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;

View File

@ -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;

View File

@ -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()
{
}

View File

@ -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 <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#else
#include <winsock.h>
#endif
#include <cstdint>
#include <string>
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

View File

@ -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");
}

View File

@ -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 <cstdint>
#include <string>
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;

View File

@ -0,0 +1,923 @@
/*
* 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 <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_address(),
m_headerSeen(false),
m_seen1(false),
m_seen2(false),
m_seen3(false),
m_seen4(false),
m_sacch(NULL),
m_sessionId(1U),
m_seqNo(0U),
m_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)
{
assert(!address.empty());
m_sacch = new unsigned char[10U];
m_address = CUDPSocket::lookup(address);
}
CKenwoodNetwork::~CKenwoodNetwork()
{
delete[] m_sacch;
}
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 = m_rtpSocket.getLocalAddress();
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_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;
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_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;
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_address, RTP_PORT);
}
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_address, RTCP_PORT);
}
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_address, RTCP_PORT);
}
bool CKenwoodNetwork::writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst)
{
m_hangType = type;
m_hangSrc = src;
m_hangDst = dst;
return writeRTCPHang();
}
bool CKenwoodNetwork::writeRTCPHang()
{
unsigned char buffer[30U];
::memset(buffer, 0x00U, 30U);
buffer[0U] = 0x8BU;
buffer[1U] = 0xCCU;
buffer[2U] = 0x00U;
buffer[3U] = 0x04U;
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
buffer[8U] = 'K';
buffer[9U] = 'W';
buffer[10U] = 'N';
buffer[11U] = 'E';
buffer[12U] = (m_hangSrc >> 8) & 0xFFU;
buffer[13U] = (m_hangSrc >> 0) & 0xFFU;
buffer[14U] = (m_hangDst >> 8) & 0xFFU;
buffer[15U] = (m_hangDst >> 0) & 0xFFU;
buffer[16U] = m_hangType;
if (m_debug)
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);
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];
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) {
LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
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];
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) {
LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
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[25U];
outData[21U] = inData[28U];
outData[22U] = inData[27U];
outData[23U] = inData[29U];
::memcpy(inData, outData, 24U);
return 24U;
}
}
unsigned long CKenwoodNetwork::getTimeStamp() const
{
unsigned long timeStamp = 0UL;
#if defined(_WIN32) || defined(_WIN64)
SYSTEMTIME st;
::GetSystemTime(&st);
unsigned int hh = st.wHour;
unsigned int mm = st.wMinute;
unsigned int ss = st.wSecond;
unsigned int ms = st.wMilliseconds;
timeStamp += hh * 3600U * 1000U * 80U;
timeStamp += mm * 60U * 1000U * 80U;
timeStamp += ss * 1000U * 80U;
timeStamp += ms * 80U;
#else
struct timeval tod;
::gettimeofday(&tod, NULL);
unsigned int ss = tod.tv_sec;
unsigned int ms = tod.tv_usec / 1000U;
timeStamp += ss * 1000U * 80U;
timeStamp += ms * 80U;
#endif
return timeStamp;
}
unsigned int CKenwoodNetwork::processKenwoodVoiceLateEntry(unsigned char* inData)
{
assert(inData != NULL);
unsigned char sacch[4U];
sacch[0U] = inData[12U];
sacch[1U] = inData[11U];
sacch[2U] = inData[14U];
sacch[3U] = inData[13U];
switch (sacch[0U] & 0xC0U) {
case 0xC0U:
if (!m_seen1) {
unsigned int offset = 0U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen1 = true;
}
break;
case 0x80U:
if (!m_seen2) {
unsigned int offset = 18U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen2 = true;
}
break;
case 0x40U:
if (!m_seen3) {
unsigned int offset = 36U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen3 = true;
}
break;
case 0x00U:
if (!m_seen4) {
unsigned int offset = 54U;
for (unsigned int i = 8U; i < 26U; i++, offset++) {
bool b = READ_BIT(sacch, i) != 0U;
WRITE_BIT(m_sacch, offset, b);
}
m_seen4 = true;
}
break;
}
if (!m_seen1 || !m_seen2 || !m_seen3 || !m_seen4)
return 0U;
// Create a dummy header
// Header SACCH
inData[11U] = 0x10U;
inData[12U] = 0x01U;
inData[13U] = 0x00U;
inData[14U] = 0x00U;
// Header FACCH
inData[15U] = m_sacch[1U];
inData[16U] = m_sacch[0U];
inData[17U] = m_sacch[3U];
inData[18U] = m_sacch[2U];
inData[19U] = m_sacch[5U];
inData[20U] = m_sacch[4U];
inData[21U] = m_sacch[7U];
inData[22U] = m_sacch[6U];
inData[23U] = 0x00U;
inData[24U] = m_sacch[8U];
return processKenwoodVoiceHeader(inData);
}

View File

@ -0,0 +1,84 @@
/*
* 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 "UDPSocket.h"
#include "Timer.h"
#include <cstdint>
#include <string>
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;
in_addr m_address;
bool m_headerSeen;
bool m_seen1;
bool m_seen2;
bool m_seen3;
bool m_seen4;
unsigned char* m_sacch;
uint8_t m_sessionId;
uint16_t m_seqNo;
unsigned int m_ssrc;
bool m_debug;
uint32_t m_startSecs;
uint32_t m_startUSecs;
CTimer m_rtcpTimer;
CTimer m_hangTimer;
unsigned char m_hangType;
unsigned short m_hangSrc;
unsigned short m_hangDst;
bool processIcomVoiceHeader(const unsigned char* data);
bool processIcomVoiceData(const unsigned char* data);
unsigned int processKenwoodVoiceHeader(unsigned char* data);
unsigned int processKenwoodVoiceData(unsigned char* data);
unsigned int processKenwoodVoiceLateEntry(unsigned char* data);
unsigned int processKenwoodData(unsigned char* data);
bool writeRTPVoiceHeader(const unsigned char* data);
bool writeRTPVoiceData(const unsigned char* data);
bool writeRTPVoiceTrailer(const unsigned char* data);
bool writeRTCPStart();
bool writeRTCPPing();
bool writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst);
bool writeRTCPHang();
unsigned int readRTP(unsigned char* data);
unsigned int readRTCP(unsigned char* data);
unsigned long getTimeStamp() const;
};
#endif

View File

@ -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

185
NXDNReflector/NXDNCRC.cpp Normal file
View File

@ -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 <cstdio>
#include <cassert>
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;
}

42
NXDNReflector/NXDNCRC.h Normal file
View File

@ -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 <cstdint>
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

View File

@ -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;

View File

@ -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<CNXDNRepeater*> m_repeaters;
CNXDNRepeater* findRepeater(const in_addr& address, unsigned int port) const;

View File

@ -19,6 +19,7 @@ Debug=0
[NXCore]
Enabled=0
Protocol=Icom
# Address=208.111.3.45
Address=44.131.4.1
# TGEnable=1234

View File

@ -20,7 +20,10 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="Conf.h" />
<ClInclude Include="NXCoreNetwork.h" />
<ClInclude Include="CoreNetwork.h" />
<ClInclude Include="IcomNetwork.h" />
<ClInclude Include="KenwoodNetwork.h" />
<ClInclude Include="NXDNCRC.h" />
<ClInclude Include="NXDNLookup.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="Mutex.h" />
@ -35,7 +38,10 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="Conf.cpp" />
<ClCompile Include="NXCoreNetwork.cpp" />
<ClCompile Include="CoreNetwork.cpp" />
<ClCompile Include="IcomNetwork.cpp" />
<ClCompile Include="KenwoodNetwork.cpp" />
<ClCompile Include="NXDNCRC.cpp" />
<ClCompile Include="NXDNLookup.cpp" />
<ClCompile Include="Log.cpp" />
<ClCompile Include="Mutex.cpp" />
@ -52,32 +58,32 @@
<ProjectGuid>{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>NXDNReflector</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@ -47,7 +47,16 @@
<ClInclude Include="NXDNNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NXCoreNetwork.h">
<ClInclude Include="IcomNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KenwoodNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CoreNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NXDNCRC.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -85,7 +94,16 @@
<ClCompile Include="NXDNNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NXCoreNetwork.cpp">
<ClCompile Include="IcomNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KenwoodNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CoreNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NXDNCRC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2016 by Jonathan Naylor G4KLX
* Copyright (C) 2006-2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -260,3 +260,31 @@ void CUDPSocket::close()
::close(m_fd);
#endif
}
unsigned long CUDPSocket::getLocalAddress() const
{
unsigned long address = 0UL;
char hostname[80U];
int ret = ::gethostname(hostname, 80);
if (ret == -1)
return 0UL;
struct hostent* phe = ::gethostbyname(hostname);
if (phe == NULL)
return 0UL;
if (phe->h_addrtype != AF_INET)
return 0UL;
for (unsigned int i = 0U; phe->h_addr_list[i] != NULL; i++) {
struct in_addr addr;
::memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
if (addr.s_addr != INADDR_LOOPBACK) {
address = addr.s_addr;
break;
}
}
return address;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2011,2013,2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -47,7 +47,9 @@ public:
void close();
static in_addr lookup(const std::string& hostName);
unsigned long getLocalAddress() const;
static in_addr lookup(const std::string& hostName);
private:
std::string m_address;

View File

@ -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 = "20200427";
#endif

View File

@ -1,15 +1,28 @@
These programs are clients for the NXDN networking now built into the MMDVM Host.
These programs are clients for the NXDN networking built into the MMDVM Host.
The Parrot is very simple minded and can only handle one client at a time and is therefore not suitable for use as a shared resource via the Internet.
The Parrot is very simple minded and can only handle one client at a time and
is therefore not suitable for use as a shared resource via the Internet.
The Reflector is used as a single talk group in the same way that it is with P25. It also includes the option to link it to NXCore to allow for interchange of audio between the two. At the NXCore end, it should be set up to receive the traffic from only one talk group.
The Reflector is used as a single talk group in the same way that it is with
P25. It also includes the option to link it to NXCore to allow for interchange
of audio between the two. At the NXCore end, it should be set up to receive the
traffic from only one talk group.
The Gateway allows for use of NXDN Talk Groups to control the access to the various NXDN reflectors. It speaks the same language as Icom repeaters to the MMDVM so could theoretically be used as a gateway for a real Icom NXDN repeater. This has not been tested.
The Gateway allows for use of NXDN Talk Groups to control the access to the
various NXDN reflectors. It speaks the same language as Icom repeaters to the
MMDVM so can be used as a gateway for Icom NXDN repeaters. It also
includes experimental support for Kenwood NXDN repeaters.
The Gateway has an ini file that contain the parameters for running the software. The filename of the ini file is passed as a parameter on the command line. The Parrot takes the UDP port number to listen on as an argument.
The Gateway has an ini file that contain the parameters for running the
software. The filename of the ini file is passed as a parameter on the command
line. The Parrot takes the UDP port number to listen on as an argument.
The MMDVM .ini file should have the IP address and port number of the client in the [NXDN Network] settings.
The MMDVM .ini file should have the IP address and port number of the client in
the [NXDN Network] settings.
They build on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 on x86 and x64.
These programs build on 32-bit and 64-bit Linux as well as on Windows using
Visual Studio 2019 on x86 and x64.
This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden.
This software is licenced under the GPL v2 and is intended for amateur and
educational use only. Use of this software for commercial purposes is strictly
forbidden.

View File

@ -1,2 +0,0 @@
# 911 911 Cop Talk
911 NXDN.k2cop.com 41400