diff --git a/NXDNGateway/APRSWriter.cpp b/NXDNGateway/APRSWriter.cpp index e5eb58e..248364b 100644 --- a/NXDNGateway/APRSWriter.cpp +++ b/NXDNGateway/APRSWriter.cpp @@ -17,29 +17,34 @@ */ #include "APRSWriter.h" +#include "Log.h" #include #include #include #include -CAPRSWriter::CAPRSWriter(const std::string& callsign, const std::string& suffix, const std::string& password, const std::string& address, unsigned int port) : -m_thread(NULL), -m_enabled(false), +CAPRSWriter::CAPRSWriter(const std::string& callsign, const std::string& suffix, const std::string& address, unsigned int port, bool debug) : m_idTimer(1000U), m_callsign(callsign), +m_debug(debug), m_txFrequency(0U), m_rxFrequency(0U), m_latitude(0.0F), m_longitude(0.0F), m_height(0), m_desc(), -m_mobileGPSAddress(), -m_mobileGPSPort(0U), -m_socket(NULL) +m_aprsAddress(), +m_aprsPort(port), +m_aprsSocket() +#if defined(USE_GPSD) +,m_gpsdEnabled(false), +m_gpsdAddress(), +m_gpsdPort(), +m_gpsdData() +#endif { assert(!callsign.empty()); - assert(!password.empty()); assert(!address.empty()); assert(port > 0U); @@ -48,7 +53,7 @@ m_socket(NULL) m_callsign.append(suffix.substr(0U, 1U)); } - m_thread = new CAPRSWriterThread(m_callsign, password, address, port); + m_aprsAddress = CUDPSocket::lookup(address); } CAPRSWriter::~CAPRSWriter() @@ -69,88 +74,92 @@ void CAPRSWriter::setStaticLocation(float latitude, float longitude, int height) m_height = height; } -void CAPRSWriter::setMobileLocation(const std::string& address, unsigned int port) +void CAPRSWriter::setGPSDLocation(const std::string& address, const std::string& port) { +#if defined(USE_GPSD) assert(!address.empty()); - assert(port > 0U); + assert(!port.empty()); - m_mobileGPSAddress = CUDPSocket::lookup(address); - m_mobileGPSPort = port; - - m_socket = new CUDPSocket; + m_gpsdEnabled = true; + m_gpsdAddress = address; + m_gpsdPort = port; +#endif } bool CAPRSWriter::open() { - if (m_socket != NULL) { - bool ret = m_socket->open(); - if (!ret) { - delete m_socket; - m_socket = NULL; +#if defined(USE_GPSD) + if (m_gpsdEnabled) { + int ret = ::gps_open(m_gpsdAddress.c_str(), m_gpsdPort.c_str(), &m_gpsdData); + if (ret != 0) { + LogError("Error when opening access to gpsd - %d - %s", errno, ::gps_errstr(errno)); return false; } - // Poll the GPS every minute - m_idTimer.setTimeout(60U); - } else { - m_idTimer.setTimeout(20U * 60U); - } + ::gps_stream(&m_gpsdData, WATCH_ENABLE | WATCH_JSON, NULL); + LogMessage("Connected to GPSD"); + } +#endif + bool ret = m_aprsSocket.open(); + if (!ret) + return false; + + LogMessage("Opened connection to the APRS Gateway"); + + m_idTimer.setTimeout(60U); m_idTimer.start(); - return m_thread->start(); + return true; } void CAPRSWriter::write(const char* data) { assert(data != NULL); - m_thread->write(data); + if (m_debug) + LogDebug("APRS ==> %s", data); + + m_aprsSocket.write((unsigned char*)data, (unsigned int)::strlen(data), m_aprsAddress, m_aprsPort); } void CAPRSWriter::clock(unsigned int ms) { m_idTimer.clock(ms); - m_thread->clock(ms); - - if (m_socket != NULL) { +#if defined(USE_GPSD) + if (m_gpsdEnabled) { if (m_idTimer.hasExpired()) { - pollGPS(); + sendIdFrameMobile(); m_idTimer.start(); } - sendIdFrameMobile(); } else { +#endif if (m_idTimer.hasExpired()) { sendIdFrameFixed(); + m_idTimer.setTimeout(20U * 60U); m_idTimer.start(); } +#if defined(USE_GPSD) } +#endif } void CAPRSWriter::close() { - if (m_socket != NULL) { - m_socket->close(); - delete m_socket; + m_aprsSocket.close(); + +#if defined(USE_GPSD) + if (m_gpsdEnabled) { + ::gps_stream(&m_gpsdData, WATCH_DISABLE, NULL); + ::gps_close(&m_gpsdData); } - - m_thread->stop(); -} - -bool CAPRSWriter::pollGPS() -{ - assert(m_socket != NULL); - - return m_socket->write((unsigned char*)"NXDNGateway", 11U, m_mobileGPSAddress, m_mobileGPSPort); +#endif } void CAPRSWriter::sendIdFrameFixed() { - if (!m_thread->isConnected()) - return; - // Default values aren't passed on if (m_latitude == 0.0F && m_longitude == 0.0F) return; @@ -201,43 +210,52 @@ void CAPRSWriter::sendIdFrameFixed() server.append("S"); char output[500U]; - ::sprintf(output, "%s>APDG04,TCPIP*,qAC,%s:!%s%cD%s%c&/A=%06.0f%s %s", + ::sprintf(output, "%s>APDG04,TCPIP*,qAC,%s:!%s%cD%s%c&/A=%06.0f%s %s\r\n", m_callsign.c_str(), server.c_str(), lat, (m_latitude < 0.0F) ? 'S' : 'N', lon, (m_longitude < 0.0F) ? 'W' : 'E', float(m_height) * 3.28F, band, desc); - m_thread->write(output); + if (m_debug) + LogDebug("APRS ==> %s", output); + + write(output); } +#if defined(USE_GPSD) void CAPRSWriter::sendIdFrameMobile() { - // Grab GPS data if it's available - unsigned char buffer[200U]; - in_addr address; - unsigned int port; - int ret = m_socket->read(buffer, 200U, address, port); - if (ret <= 0) + if (!::gps_waiting(&m_gpsdData, 0)) return; - if (!m_thread->isConnected()) +#if GPSD_API_MAJOR_VERSION >= 7 + if (::gps_read(&m_gpsdData, NULL, 0) <= 0) + return; +#else + if (::gps_read(&m_gpsdData) <= 0) + return; +#endif + + if (m_gpsdData.status != STATUS_FIX) return; - buffer[ret] = '\0'; + bool latlonSet = (m_gpsdData.set & LATLON_SET) == LATLON_SET; + bool altitudeSet = (m_gpsdData.set & ALTITUDE_SET) == ALTITUDE_SET; + bool velocitySet = (m_gpsdData.set & SPEED_SET) == SPEED_SET; + bool bearingSet = (m_gpsdData.set & TRACK_SET) == TRACK_SET; - // Parse the GPS data - char* pLatitude = ::strtok((char*)buffer, ",\n"); // Latitude - char* pLongitude = ::strtok(NULL, ",\n"); // Longitude - char* pAltitude = ::strtok(NULL, ",\n"); // Altitude (m) - char* pVelocity = ::strtok(NULL, ",\n"); // Velocity (kms/h) - char* pBearing = ::strtok(NULL, "\n"); // Bearing - - if (pLatitude == NULL || pLongitude == NULL || pAltitude == NULL) + if (!latlonSet) return; - float rawLatitude = float(::atof(pLatitude)); - float rawLongitude = float(::atof(pLongitude)); - float rawAltitude = float(::atof(pAltitude)); + float rawLatitude = float(m_gpsdData.fix.latitude); + float rawLongitude = float(m_gpsdData.fix.longitude); +#if GPSD_API_MAJOR_VERSION >= 9 + float rawAltitude = float(m_gpsdData.fix.altMSL); +#else + float rawAltitude = float(m_gpsdData.fix.altitude); +#endif + float rawVelocity = float(m_gpsdData.fix.speed); + float rawBearing = float(m_gpsdData.fix.track); char desc[200U]; if (m_txFrequency != 0U) { @@ -290,15 +308,17 @@ void CAPRSWriter::sendIdFrameMobile() lat, (rawLatitude < 0.0F) ? 'S' : 'N', lon, (rawLongitude < 0.0F) ? 'W' : 'E'); - if (pBearing != NULL && pVelocity != NULL) { - float rawBearing = float(::atof(pBearing)); - float rawVelocity = float(::atof(pVelocity)); - + if (bearingSet && velocitySet) ::sprintf(output + ::strlen(output), "%03.0f/%03.0f", rawBearing, rawVelocity * 0.539957F); - } - ::sprintf(output + ::strlen(output), "/A=%06.0f%s %s", float(rawAltitude) * 3.28F, band, desc); + if (altitudeSet) + ::sprintf(output + ::strlen(output), "/A=%06.0f", float(rawAltitude) * 3.28F); - m_thread->write(output); + ::sprintf(output + ::strlen(output), "%s %s\r\n", band, desc); + + if (m_debug) + LogDebug("APRS ==> %s", output); + + write(output); } - +#endif diff --git a/NXDNGateway/APRSWriter.h b/NXDNGateway/APRSWriter.h index 0ea1ad6..f29d9a5 100644 --- a/NXDNGateway/APRSWriter.h +++ b/NXDNGateway/APRSWriter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010,2011,2012,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010,2011,2012,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,7 +19,6 @@ #ifndef APRSWriter_H #define APRSWriter_H -#include "APRSWriterThread.h" #include "UDPSocket.h" #include "Timer.h" @@ -34,13 +33,16 @@ #include #include #include +#if defined(USE_GPSD) +#include +#endif #else #include #endif class CAPRSWriter { public: - CAPRSWriter(const std::string& callsign, const std::string& suffix, const std::string& password, const std::string& address, unsigned int port); + CAPRSWriter(const std::string& callsign, const std::string& suffix, const std::string& address, unsigned int port, bool debug); ~CAPRSWriter(); bool open(); @@ -49,7 +51,7 @@ public: void setStaticLocation(float latitude, float longitude, int height); - void setMobileLocation(const std::string& address, unsigned int port); + void setGPSDLocation(const std::string& address, const std::string& port); void write(const char* data); @@ -58,21 +60,25 @@ public: void close(); private: - CAPRSWriterThread* m_thread; - bool m_enabled; - CTimer m_idTimer; - std::string m_callsign; - unsigned int m_txFrequency; - unsigned int m_rxFrequency; - float m_latitude; - float m_longitude; - int m_height; - std::string m_desc; - in_addr m_mobileGPSAddress; - unsigned int m_mobileGPSPort; - CUDPSocket* m_socket; + CTimer m_idTimer; + std::string m_callsign; + bool m_debug; + unsigned int m_txFrequency; + unsigned int m_rxFrequency; + float m_latitude; + float m_longitude; + int m_height; + std::string m_desc; + in_addr m_aprsAddress; + unsigned int m_aprsPort; + CUDPSocket m_aprsSocket; +#if defined(USE_GPSD) + bool m_gpsdEnabled; + std::string m_gpsdAddress; + std::string m_gpsdPort; + struct gps_data_t m_gpsdData; +#endif - bool pollGPS(); void sendIdFrameFixed(); void sendIdFrameMobile(); }; diff --git a/NXDNGateway/APRSWriterThread.cpp b/NXDNGateway/APRSWriterThread.cpp deleted file mode 100644 index c388609..0000000 --- a/NXDNGateway/APRSWriterThread.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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 - * 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 "APRSWriterThread.h" -#include "Utils.h" -#include "Log.h" - -#include -#include -#include -#include -#include -#include - -// #define DUMP_TX - -const unsigned int CALLSIGN_LENGTH = 8U; - -const unsigned int APRS_TIMEOUT = 10U; - -CAPRSWriterThread::CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, unsigned int port) : -CThread(), -m_username(callsign), -m_password(password), -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") -{ - assert(!callsign.empty()); - assert(!password.empty()); - assert(!address.empty()); - assert(port > 0U); - - m_username.resize(CALLSIGN_LENGTH, ' '); - m_username.erase(std::find_if(m_username.rbegin(), m_username.rend(), std::not1(std::ptr_fun(std::isspace))).base(), m_username.end()); - std::transform(m_username.begin(), m_username.end(), m_username.begin(), ::toupper); -} - -CAPRSWriterThread::CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, unsigned int port, const std::string& filter, const std::string& clientName) : -CThread(), -m_username(callsign), -m_password(password), -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) -{ - assert(!callsign.empty()); - assert(!password.empty()); - assert(!address.empty()); - assert(port > 0U); - - m_username.resize(CALLSIGN_LENGTH, ' '); - m_username.erase(std::find_if(m_username.rbegin(), m_username.rend(), std::not1(std::ptr_fun(std::isspace))).base(), m_username.end()); - std::transform(m_username.begin(), m_username.end(), m_username.begin(), ::toupper); -} - -CAPRSWriterThread::~CAPRSWriterThread() -{ - m_username.clear(); -} - -bool CAPRSWriterThread::start() -{ - run(); - - return true; -} - -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) { - if (m_reconnectTimer.isRunning() && m_reconnectTimer.hasExpired()) { - m_reconnectTimer.stop(); - - 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); - - LogMessage("APRS ==> %s", p); - - ::strcat(p, "\r\n"); - - 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; - } - { - std::string line; - int length = m_socket.readLine(line, APRS_TIMEOUT); - - if (length < 0) { - 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 - && m_APRSReadCallback != NULL)//do we have someone wanting an APRS Frame? - { - //wxLogMessage(wxT("Received APRS Frame : ") + line); - m_APRSReadCallback(std::string(line)); - } - } - - } - } - - if (m_connected) - m_socket.close(); - - while (!m_queue.isEmpty()) { - char* p = NULL; - m_queue.getData(&p, 1U); - delete[] p; - } - } - catch (std::exception& e) { - LogError("Exception raised in the APRS Writer thread - \"%s\"", e.what()); - } - catch (...) { - LogError("Unknown exception raised in the APRS Writer thread"); - } - - LogMessage("Stopping the APRS Writer thread"); -} - -void CAPRSWriterThread::setReadAPRSCallback(ReadAPRSFrameCallback cb) -{ - m_APRSReadCallback = cb; -} - -void CAPRSWriterThread::write(const char* data) -{ - assert(data != NULL); - - if (!m_connected) - return; - - unsigned int len = (unsigned int)::strlen(data); - - char* p = new char[len + 5U]; - ::strcpy(p, data); - - m_queue.addData(&p, 1U); -} - -bool CAPRSWriterThread::isConnected() const -{ - return m_connected; -} - -void CAPRSWriterThread::stop() -{ - m_exit = true; - - wait(); -} - -void CAPRSWriterThread::clock(unsigned int ms) -{ - m_reconnectTimer.clock(ms); -} - -bool CAPRSWriterThread::connect() -{ - bool ret = m_socket.open(); - if (!ret) - return false; - - //wait for lgin banner - int length; - std::string serverResponse; - length = m_socket.readLine(serverResponse, APRS_TIMEOUT); - if (length == 0) { - LogError("No reply from the APRS server after %u seconds", APRS_TIMEOUT); - m_socket.close(); - return false; - } - - LogMessage("Received login banner : %s", serverResponse.c_str()); - - std::string filter(m_filter); - if (filter.length() > 0) - filter.insert(0U, " filter "); - - char connectString[200U]; - ::sprintf(connectString, "user %s pass %s vers %s%s\n", m_username.c_str(), m_password.c_str(), (m_clientName.length() ? m_clientName : "YSFGateway").c_str(), filter.c_str()); - - ret = m_socket.writeLine(std::string(connectString)); - if (!ret) { - m_socket.close(); - return false; - } - - length = m_socket.readLine(serverResponse, APRS_TIMEOUT); - if (length == 0) { - LogError("No reply from the APRS server after %u seconds", APRS_TIMEOUT); - m_socket.close(); - return false; - } - - if (length < 0) { - LogError("Error when reading from the APRS server"); - m_socket.close(); - return false; - } - - LogMessage("Response from APRS server: %s", serverResponse.c_str()); - - LogMessage("Connected to the APRS server"); - - 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(); -} diff --git a/NXDNGateway/APRSWriterThread.h b/NXDNGateway/APRSWriterThread.h deleted file mode 100644 index c86c23a..0000000 --- a/NXDNGateway/APRSWriterThread.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 - * 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 APRSWriterThread_H -#define APRSWriterThread_H - -#include "TCPSocket.h" -#include "RingBuffer.h" -#include "Timer.h" -#include "Thread.h" - -#include - -typedef void (*ReadAPRSFrameCallback)(const std::string&); - -class CAPRSWriterThread : public CThread { -public: - CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, unsigned int port); - CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, unsigned int port, const std::string& filter, const std::string& clientName); - virtual ~CAPRSWriterThread(); - - virtual bool start(); - - virtual bool isConnected() const; - - virtual void write(const char* data); - - virtual void entry(); - - virtual void stop(); - - void setReadAPRSCallback(ReadAPRSFrameCallback cb); - - void clock(unsigned int ms); - -private: - std::string m_username; - std::string m_password; - CTCPSocket m_socket; - CRingBuffer 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 diff --git a/NXDNGateway/Conf.cpp b/NXDNGateway/Conf.cpp index 446be3d..df80dd1 100644 --- a/NXDNGateway/Conf.cpp +++ b/NXDNGateway/Conf.cpp @@ -33,9 +33,9 @@ enum SECTION { SECTION_ID_LOOKUP, SECTION_VOICE, SECTION_LOG, - SECTION_APRS_FI, + SECTION_APRS, SECTION_NETWORK, - SECTION_MOBILE_GPS, + SECTION_GPSD, SECTION_REMOTE_COMMANDS }; @@ -47,7 +47,7 @@ m_rptProtocol("Icom"), m_rptAddress(), m_rptPort(0U), m_myPort(0U), -m_rptDebug(false), +m_debug(false), m_daemon(false), m_rxFrequency(0U), m_txFrequency(0U), @@ -65,9 +65,8 @@ m_voiceDirectory(), m_logFilePath(), m_logFileRoot(), m_aprsEnabled(false), -m_aprsServer(), -m_aprsPort(0U), -m_aprsPassword(), +m_aprsAddress("127.0.0.1"), +m_aprsPort(8673U), m_aprsSuffix(), m_aprsDescription(), m_networkPort(0U), @@ -81,9 +80,9 @@ m_networkNXDN2DMRPort(0U), m_networkStartup(9999U), m_networkInactivityTimeout(0U), m_networkDebug(false), -m_mobileGPSEnabled(false), -m_mobileGPSAddress(), -m_mobileGPSPort(0U), +m_gpsdEnabled(false), +m_gpsdAddress(), +m_gpsdPort(), m_remoteCommandsEnabled(false), m_remoteCommandsPort(6075U) { @@ -119,12 +118,12 @@ bool CConf::read() section = SECTION_VOICE; else if (::strncmp(buffer, "[Log]", 5U) == 0) section = SECTION_LOG; - else if (::strncmp(buffer, "[aprs.fi]", 9U) == 0) - section = SECTION_APRS_FI; + else if (::strncmp(buffer, "[APRS]", 6U) == 0) + section = SECTION_APRS; else if (::strncmp(buffer, "[Network]", 9U) == 0) section = SECTION_NETWORK; - else if (::strncmp(buffer, "[Mobile GPS]", 12U) == 0) - section = SECTION_MOBILE_GPS; + else if (::strncmp(buffer, "[GPSD]", 6U) == 0) + section = SECTION_GPSD; else if (::strncmp(buffer, "[Remote Commands]", 17U) == 0) section = SECTION_REMOTE_COMMANDS; else @@ -158,7 +157,7 @@ bool CConf::read() else if (::strcmp(key, "LocalPort") == 0) m_myPort = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) - m_rptDebug = ::atoi(value) == 1; + m_debug = ::atoi(value) == 1; else if (::strcmp(key, "Daemon") == 0) m_daemon = ::atoi(value) == 1; } else if (section == SECTION_INFO) { @@ -195,15 +194,13 @@ bool CConf::read() m_logFilePath = value; else if (::strcmp(key, "FileRoot") == 0) m_logFileRoot = value; - } else if (section == SECTION_APRS_FI) { + } else if (section == SECTION_APRS) { if (::strcmp(key, "Enable") == 0) m_aprsEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Server") == 0) - m_aprsServer = value; + else if (::strcmp(key, "Address") == 0) + m_aprsAddress = value; else if (::strcmp(key, "Port") == 0) m_aprsPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "Password") == 0) - m_aprsPassword = value; else if (::strcmp(key, "Suffix") == 0) m_aprsSuffix = value; else if (::strcmp(key, "Description") == 0) @@ -231,13 +228,13 @@ bool CConf::read() m_networkInactivityTimeout = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_networkDebug = ::atoi(value) == 1; - } else if (section == SECTION_MOBILE_GPS) { + } else if (section == SECTION_GPSD) { if (::strcmp(key, "Enable") == 0) - m_mobileGPSEnabled = ::atoi(value) == 1; + m_gpsdEnabled = ::atoi(value) == 1; else if (::strcmp(key, "Address") == 0) - m_mobileGPSAddress = value; + m_gpsdAddress = value; else if (::strcmp(key, "Port") == 0) - m_mobileGPSPort = (unsigned int)::atoi(value); + m_gpsdPort = value; } else if (section == SECTION_REMOTE_COMMANDS) { if (::strcmp(key, "Enable") == 0) m_remoteCommandsEnabled = ::atoi(value) == 1; @@ -281,9 +278,9 @@ unsigned int CConf::getMyPort() const return m_myPort; } -bool CConf::getRptDebug() const +bool CConf::getDebug() const { - return m_rptDebug; + return m_debug; } bool CConf::getDaemon() const @@ -366,9 +363,9 @@ bool CConf::getAPRSEnabled() const return m_aprsEnabled; } -std::string CConf::getAPRSServer() const +std::string CConf::getAPRSAddress() const { - return m_aprsServer; + return m_aprsAddress; } unsigned int CConf::getAPRSPort() const @@ -376,11 +373,6 @@ unsigned int CConf::getAPRSPort() const return m_aprsPort; } -std::string CConf::getAPRSPassword() const -{ - return m_aprsPassword; -} - std::string CConf::getAPRSSuffix() const { return m_aprsSuffix; @@ -451,19 +443,19 @@ bool CConf::getNetworkDebug() const return m_networkDebug; } -bool CConf::getMobileGPSEnabled() const +bool CConf::getGPSDEnabled() const { - return m_mobileGPSEnabled; + return m_gpsdEnabled; } -std::string CConf::getMobileGPSAddress() const +std::string CConf::getGPSDAddress() const { - return m_mobileGPSAddress; + return m_gpsdAddress; } -unsigned int CConf::getMobileGPSPort() const +std::string CConf::getGPSDPort() const { - return m_mobileGPSPort; + return m_gpsdPort; } bool CConf::getRemoteCommandsEnabled() const diff --git a/NXDNGateway/Conf.h b/NXDNGateway/Conf.h index 439727e..5700b40 100644 --- a/NXDNGateway/Conf.h +++ b/NXDNGateway/Conf.h @@ -37,7 +37,7 @@ public: std::string getRptAddress() const; unsigned int getRptPort() const; unsigned int getMyPort() const; - bool getRptDebug() const; + bool getDebug() const; bool getDaemon() const; // The Info section @@ -59,11 +59,10 @@ public: std::string getVoiceLanguage() const; std::string getVoiceDirectory() const; - // The aprs.fi section + // The APRS section bool getAPRSEnabled() const; - std::string getAPRSServer() const; + std::string getAPRSAddress() const; unsigned int getAPRSPort() const; - std::string getAPRSPassword() const; std::string getAPRSSuffix() const; std::string getAPRSDescription() const; @@ -84,10 +83,10 @@ public: unsigned int getNetworkInactivityTimeout() const; bool getNetworkDebug() const; - // The Mobile GPS section - bool getMobileGPSEnabled() const; - std::string getMobileGPSAddress() const; - unsigned int getMobileGPSPort() const; + // The GPSD section + bool getGPSDEnabled() const; + std::string getGPSDAddress() const; + std::string getGPSDPort() const; // The Remote Commands section bool getRemoteCommandsEnabled() const; @@ -101,7 +100,7 @@ private: std::string m_rptAddress; unsigned int m_rptPort; unsigned int m_myPort; - bool m_rptDebug; + bool m_debug; bool m_daemon; unsigned int m_rxFrequency; @@ -124,9 +123,8 @@ private: std::string m_logFileRoot; bool m_aprsEnabled; - std::string m_aprsServer; + std::string m_aprsAddress; unsigned int m_aprsPort; - std::string m_aprsPassword; std::string m_aprsSuffix; std::string m_aprsDescription; @@ -142,9 +140,9 @@ private: unsigned int m_networkInactivityTimeout; bool m_networkDebug; - bool m_mobileGPSEnabled; - std::string m_mobileGPSAddress; - unsigned int m_mobileGPSPort; + bool m_gpsdEnabled; + std::string m_gpsdAddress; + std::string m_gpsdPort; bool m_remoteCommandsEnabled; unsigned int m_remoteCommandsPort; diff --git a/NXDNGateway/GPSHandler.cpp b/NXDNGateway/GPSHandler.cpp index e0a511f..4a81e7c 100644 --- a/NXDNGateway/GPSHandler.cpp +++ b/NXDNGateway/GPSHandler.cpp @@ -140,10 +140,10 @@ bool CGPSHandler::processIcom() 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 Icom IDAS via MMDVM", + ::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%07.2lf%s/%08.2lf%sr%03d/%03d Icom IDAS via MMDVM\r\n", 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 Icom IDAS via MMDVM", + ::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%07.2lf%s/%08.2lf%sr Icom IDAS via MMDVM\r\n", source.c_str(), m_callsign.c_str(), latitude, pRMC[4U], longitude, pRMC[6U]); } @@ -239,10 +239,10 @@ bool CGPSHandler::processKenwood() 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", + ::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%04u.%02u%c/%05u.%02u%cr%03d/%03d Kenwood NEXEDGE via MMDVM\r\n", 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", + ::sprintf(output, "%s>APDPRS,NXDN*,qAR,%s:!%04u.%02u%c/%05u.%02u%cr Kenwood NEXEDGE via MMDVM\r\n", source.c_str(), m_callsign.c_str(), latBefore, latAfter / 100U, north, lonBefore, lonAfter / 100U, east); } diff --git a/NXDNGateway/KenwoodNetwork.cpp b/NXDNGateway/KenwoodNetwork.cpp index c5a4a20..28ab2e2 100644 --- a/NXDNGateway/KenwoodNetwork.cpp +++ b/NXDNGateway/KenwoodNetwork.cpp @@ -55,7 +55,8 @@ m_rtcpTimer(1000U, 0U, 200U), m_hangTimer(1000U, 5U), m_hangType(0U), m_hangSrc(0U), -m_hangDst(0U) +m_hangDst(0U), +m_random() { assert(localPort > 0U); assert(!rptAddress.empty()); @@ -64,6 +65,10 @@ m_hangDst(0U) m_sacch = new unsigned char[10U]; m_address = CUDPSocket::lookup(rptAddress); + + std::random_device rd; + std::mt19937 mt(rd()); + m_random = mt; } CKenwoodNetwork::~CKenwoodNetwork() @@ -86,7 +91,8 @@ bool CKenwoodNetwork::open() return false; } - m_ssrc = m_rtpSocket.getLocalAddress(); + std::uniform_int_distribution dist(0x00000001, 0xfffffffe); + m_ssrc = dist(m_random); return true; } diff --git a/NXDNGateway/KenwoodNetwork.h b/NXDNGateway/KenwoodNetwork.h index aa52d64..b0e1183 100644 --- a/NXDNGateway/KenwoodNetwork.h +++ b/NXDNGateway/KenwoodNetwork.h @@ -25,6 +25,7 @@ #include #include +#include class CKenwoodNetwork : public IRptNetwork { public: @@ -64,6 +65,7 @@ private: unsigned char m_hangType; unsigned short m_hangSrc; unsigned short m_hangDst; + std::mt19937 m_random; bool processIcomVoiceHeader(const unsigned char* data); bool processIcomVoiceData(const unsigned char* data); diff --git a/NXDNGateway/Makefile b/NXDNGateway/Makefile index 4ec984b..df938eb 100644 --- a/NXDNGateway/Makefile +++ b/NXDNGateway/Makefile @@ -1,11 +1,18 @@ CC = gcc CXX = g++ + +# Use the following CFLAGS and LIBS if you don't want to use gpsd. CFLAGS = -g -O3 -Wall -std=c++0x -pthread LIBS = -lpthread + +# Use the following CFLAGS and LIBS if you do want to use gpsd. +#CFLAGS = -g -O3 -Wall -DUSE_GPSD -std=c++0x -pthread +#LIBS = -lpthread -lgps + LDFLAGS = -g -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 +OBJECTS = APRSWriter.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 Thread.o Timer.o UDPSocket.o Utils.o Voice.o all: NXDNGateway @@ -15,6 +22,9 @@ NXDNGateway: $(OBJECTS) %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< +install: + install -m 755 NXDNGateway /usr/local/bin/ + clean: $(RM) NXDNGateway *.o *.d *.bak *~ - \ No newline at end of file + diff --git a/NXDNGateway/NXDNGateway.cpp b/NXDNGateway/NXDNGateway.cpp index e614f9a..5277b07 100644 --- a/NXDNGateway/NXDNGateway.cpp +++ b/NXDNGateway/NXDNGateway.cpp @@ -184,9 +184,9 @@ void CNXDNGateway::run() std::string protocol = m_conf.getRptProtocol(); if (protocol == "Kenwood") - localNetwork = new CKenwoodNetwork(m_conf.getMyPort(), m_conf.getRptAddress(), m_conf.getRptPort(), m_conf.getRptDebug()); + localNetwork = new CKenwoodNetwork(m_conf.getMyPort(), m_conf.getRptAddress(), m_conf.getRptPort(), m_conf.getDebug()); else - localNetwork = new CIcomNetwork(m_conf.getMyPort(), m_conf.getRptAddress(), m_conf.getRptPort(), m_conf.getRptDebug()); + localNetwork = new CIcomNetwork(m_conf.getMyPort(), m_conf.getRptAddress(), m_conf.getRptPort(), m_conf.getDebug()); ret = localNetwork->open(); if (!ret) { @@ -591,12 +591,12 @@ void CNXDNGateway::createGPS() std::string callsign = m_conf.getCallsign(); std::string rptSuffix = m_conf.getSuffix(); - std::string hostname = m_conf.getAPRSServer(); + std::string address = m_conf.getAPRSAddress(); unsigned int port = m_conf.getAPRSPort(); - std::string password = m_conf.getAPRSPassword(); std::string suffix = m_conf.getAPRSSuffix(); + bool debug = m_conf.getDebug(); - m_writer = new CAPRSWriter(callsign, rptSuffix, password, hostname, port); + m_writer = new CAPRSWriter(callsign, rptSuffix, address, port, debug); unsigned int txFrequency = m_conf.getTxFrequency(); unsigned int rxFrequency = m_conf.getRxFrequency(); @@ -604,12 +604,12 @@ void CNXDNGateway::createGPS() m_writer->setInfo(txFrequency, rxFrequency, desc); - bool enabled = m_conf.getMobileGPSEnabled(); + bool enabled = m_conf.getGPSDEnabled(); if (enabled) { - std::string address = m_conf.getMobileGPSAddress(); - unsigned int port = m_conf.getMobileGPSPort(); + std::string address = m_conf.getGPSDAddress(); + std::string port = m_conf.getGPSDPort(); - m_writer->setMobileLocation(address, port); + m_writer->setGPSDLocation(address, port); } else { float latitude = m_conf.getLatitude(); float longitude = m_conf.getLongitude(); diff --git a/NXDNGateway/NXDNGateway.ini b/NXDNGateway/NXDNGateway.ini index 205857c..d0aba7b 100644 --- a/NXDNGateway/NXDNGateway.ini +++ b/NXDNGateway/NXDNGateway.ini @@ -34,14 +34,12 @@ Enabled=1 Language=en_GB Directory=./Audio -[aprs.fi] +[APRS] Enable=0 -# Server=noam.aprs2.net -Server=euro.aprs2.net -Port=14580 -Password=9999 -Description=APRS Description +Address=127.0.0.1 +Port=8673 Suffix=N +Description=APRS Description [Id Lookup] Name=NXDN.csv @@ -64,10 +62,10 @@ Startup=10200 InactivityTimeout=10 Debug=0 -[Mobile GPS] +[GPSD] Enable=0 Address=127.0.0.1 -Port=7834 +Port=2947 [Remote Commands] Enable=0 diff --git a/NXDNGateway/NXDNGateway.vcxproj b/NXDNGateway/NXDNGateway.vcxproj index e955110..fae0cf4 100644 --- a/NXDNGateway/NXDNGateway.vcxproj +++ b/NXDNGateway/NXDNGateway.vcxproj @@ -147,7 +147,6 @@ - @@ -162,7 +161,6 @@ - @@ -172,7 +170,6 @@ - @@ -186,7 +183,6 @@ - diff --git a/NXDNGateway/NXDNGateway.vcxproj.filters b/NXDNGateway/NXDNGateway.vcxproj.filters index 4215f7e..e34f2d5 100644 --- a/NXDNGateway/NXDNGateway.vcxproj.filters +++ b/NXDNGateway/NXDNGateway.vcxproj.filters @@ -62,12 +62,6 @@ Header Files - - Header Files - - - Header Files - Header Files @@ -130,12 +124,6 @@ Source Files - - Source Files - - - Source Files - Source Files diff --git a/NXDNGateway/NXDNHosts.txt b/NXDNGateway/NXDNHosts.txt index 06208ca..32b2a19 100644 --- a/NXDNGateway/NXDNHosts.txt +++ b/NXDNGateway/NXDNHosts.txt @@ -2,28 +2,32 @@ # # 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 HELLAS Zone 202 hellaszone.com 41400 -# RED NXDN SPAIN +# 214 RED SPAIN 214 nxdn214.xreflector.es 41400 -# 302, P25 Canada +# 260 HBLink Poland +260 nxdn.hblink.pl 41400 + +# 302 P25 Canada 302 p25canada.hopto.org 41400 -# Super Freq 420 +# 420 Super Freq 420 hb.superfreqdigital.com 41400 -# VKCore, 505 +# 505 VKCore 505 43.229.63.42 41450 -# New Zealand, 530 +# 530 New Zealand 530 zldigitalreflectors.hopto.org 41400 -# XLX545E PA NXDN +# XLX545E PA 545 70.44.20.24 41401 -# RuralMN Reflector 707 +# 707 RuralMN Reflector 707 707nxdn.kd0ioe.com 41400 # 911 Cop Talk, Multimode TG for Police and First Responders @@ -32,7 +36,7 @@ # 914 Latin America 914 164.132.96.157 41400 -# Florida, 1200 +# 1200 Florida 1200 florida.nxref.org 41400 # 2140 DMRplus BM Multi @@ -41,10 +45,13 @@ # 2225 IT Tuscany 2225 80.211.99.134 41400 +# 2231 IT Multiprotocollo Sardegna +2231 nxdn.is0.org 41400 + # 2249 IT SICILIA 2249 nxdn.digitalsicilia.it 41400 -# RU DMR TG2503 +# 2503 RU DMR 2503 nxdn.r1ik.ru 41400 # 3023 Ontario Crosslink @@ -53,31 +60,31 @@ # 3142 Pennsylvania 3142 3.215.215.169 41402 -# VK7 TAS, 5057 +# 5057 VK7 TAS 5057 45.248.50.37 41400 -# Multiprotocolo Argentina +# 7225 Multiprotocolo Argentina 7225 ysfarg.ddns.net 41400 -# North America, 10200 +# 10200 North America 10200 dvswitch.org 42400 -# HBLINK Espana, 10301 +# 10301 HBLINK Espana 10301 ea5gvk.duckdns.org 41400 -# NXDN 10302 Multimode BM 21461 EA Spain +# 10302 Multimode BM 21461 EA Spain 10302 93.186.254.219 41400 -# Italian speaking, 10303 +# 10303 Italian speaking 10303 185.203.118.8 41400 -# REM-ADER Spain Group, 10304 +# 10304 REM-ADER Spain Group 10304 176.31.161.9 41400 # Pacific, 10400 10400 pacific.nxref.org 41400 -# Europe, German speaking, 20000 +# 20000 Europe, German speaking 20000 89.185.97.38 41400 # 20945 Deutschland DL1BH @@ -92,118 +99,125 @@ # 22202 IT NXDN Sardegna 22202 nxdn.is0hha.hblink.it 41400 -# 22220 IT ECHOLINK ITALY -22220 dagobah.hblink.it 41400 +# 22221 HBLINK IT-DMR REGIONALE LOMBARDIA +22221 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 +# 23531 RayNet UK +23531 51.91.78.118 41400 + +# 23551 NXDN Scotland 23551 nxdnscotland.ddns.net 41400 -# NX Core, 25000 +# 25000 NX Core 25000 173.166.94.77 41400 -# Russia NXDN Net, 25641 +# 25641 Russia NXDN Net 25641 194.182.85.217 41400 -# Poland, 26000 -26000 31.0.161.238 41400 +# 26000 Poland +26000 80.211.249.221 41400 -# Deutschland, 26200 +# 26200 Deutschland 26200 26200.ham-nxdn.de 41400 -# Portuguese speaking test, 10268 +# 26810 Portuguese speaking test 26810 84.90.7.110 41400 -# America-Ragchew, 28299 +# 28299 America-Ragchew 28299 65.101.7.52 41400 -# NorCal-Bridge / Multimode-NXDN, 30639 +# 30639 NorCal-Bridge / Multimode-NXDN 30639 wiresxdigi.dyndns.org 41400 -# Alabama-Link, 31010 +# 31010 Alabama-Link 31010 nxdn.alabamalink.info 41400 -# Colorado Fun Machine WE0FUN Bridge to C4FM, DMR, DStar, P25 and AllStarLink (Analog) http://www.we0fun.com +# 31081 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 Colorado HD 31088 54.191.50.212 41400 -# Connecticut Chat, 31092 +# 31092 Connecticut Chat 31092 nxdn.alecwasserman.com 41400 -# Kingsland Digital Link +# 31137 Kingsland Digital Link 31137 74.91.119.94 41400 -# Illinois, 31171 +# 31171 Illinois 31171 74.208.235.115 41400 -# Southern Indiana, 31188 +# 31188 Southern Indiana 31188 w9windigital.org 41400 # 31264 XLX625 The BROniverse www.wa8bro.com 31264 nxdn.dudetronics.com 41400 -# Central New Jersey, 31340 +# 31340 Central New Jersey 31340 cnjham.msmts.com 41400 -# Oklahoma Link, 31403 +# 31403 Oklahoma Link 31403 3.208.70.29 41400 -# XLX545A Pennsylvania Cross Mode, 31425 +# 31425 XLX545A Pennsylvania Cross Mode 31425 70.44.20.24 41400 -# XLX545A Pennsylvania Cross Mode (alt), 31426 +# 31426 XLX545A Pennsylvania Cross Mode (alt) 31426 3.215.215.169 41400 -# Rhode Island Digital Link, 31444 +# 31444 Rhode Island Digital Link 31444 18.219.32.21 41400 -# TGIF Network, 31665 +# 31665 TGIF Network 31665 tgif.network 41400 -# Pi-Star NXDN Reflector, 31672 +# 31672 Pi-Star 31672 nxdn-31672.pistar.uk 41400 -# DX-LINK SYSTEM, 31777 +# 31777 DX-LINK SYSTEM, 31777 8.9.4.102 41400 -# CW-Ops Academy, NXDN Reflector 32103 +# 31983 K8JTK Hub Multimode ILS/DVMIS (K8JTK.org) +31983 NXDNReflector31983.K8JTK.org 41400 + +# 32103 CW-Ops Academy 32103 cwops.dyndns.org 41400 -# OMISS Group, NXDN Reflector 33581 +# 33581 OMISS Group 33581 omiss.dyndns.org 41400 -# Fusion Canada Fr /Wires-x /Ysf /Nxdn Network +# 40721 Fusion Canada Fr /Wires-x /Ysf /Nxdn Network 40721 38.110.97.161 41400 -# China, 46000 +# 46000 China 46000 120.234.41.144 41400 -# DMR TG50525 bridge, 50525 +# 50525 DMR TG50525 bridge 50525 50525.nxref.org 41400 -# BM NXCore bridge, 50599 +# 50599 BM NXCore bridge 50599 nxdn.duckdns.org 41490 -# Thailand DTDXA XLX520N, 52000 +# 52000 Thailand DTDXA XLX520N 52000 nxdn.dtdxa.com 41400 -# New Zealand, 53099 linked to BM TG 53099 +# 53099 New Zealand, 53099 linked to BM TG 53099 53099 203.86.206.49 41400 -# World Wide, 65000 +# 65000 World Wide 65000 176.9.1.168 41400 -# French-Test, 65208 +# 65208 French-Test 65208 m55.evxonline.net 41400 -# NXDN - 2007DXGROUP +# 65100 NXDN - 2007DXGROUP 65100 89.46.75.115 41400 -# NXDN SPAIN 21410 +# 21410 NXDN SPAIN 21410 nxdn21410.nxdn.es 41400 diff --git a/NXDNGateway/TCPSocket.cpp b/NXDNGateway/TCPSocket.cpp deleted file mode 100644 index 9bfc3c0..0000000 --- a/NXDNGateway/TCPSocket.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2010-2013,2016 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 "TCPSocket.h" -#include "UDPSocket.h" -#include "Log.h" - -#include -#include -#include - -#if defined(_WIN32) || defined(_WIN64) -typedef int ssize_t; -#else -#include -#endif - -CTCPSocket::CTCPSocket(const std::string& address, unsigned int port) : -m_address(address), -m_port(port), -m_fd(-1) -{ - assert(!address.empty()); - assert(port > 0U); - -#if defined(_WIN32) || defined(_WIN64) - WSAData data; - int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); - if (wsaRet != 0) - LogError("Error from WSAStartup"); -#endif -} - -CTCPSocket::~CTCPSocket() -{ -#if defined(_WIN32) || defined(_WIN64) - ::WSACleanup(); -#endif -} - -bool CTCPSocket::open() -{ - if (m_fd != -1) - return true; - - if (m_address.empty() || m_port == 0U) - return false; - - m_fd = ::socket(PF_INET, SOCK_STREAM, 0); - if (m_fd < 0) { -#if defined(_WIN32) || defined(_WIN64) - LogError("Cannot create the TCP client socket, err=%d", ::GetLastError()); -#else - LogError("Cannot create the TCP client socket, err=%d", errno); -#endif - return false; - } - - struct sockaddr_in addr; - ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); - addr.sin_family = AF_INET; - addr.sin_port = htons(m_port); - addr.sin_addr = CUDPSocket::lookup(m_address); - - if (addr.sin_addr.s_addr == INADDR_NONE) { - close(); - return false; - } - - if (::connect(m_fd, (sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { -#if defined(_WIN32) || defined(_WIN64) - LogError("Cannot connect the TCP client socket, err=%d", ::GetLastError()); -#else - LogError("Cannot connect the TCP client socket, err=%d", errno); -#endif - close(); - return false; - } - - int noDelay = 1; - if (::setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&noDelay, sizeof(noDelay)) == -1) { -#if defined(_WIN32) || defined(_WIN64) - LogError("Cannot set the TCP client socket option, err=%d", ::GetLastError()); -#else - LogError("Cannot set the TCP client socket option, err=%d", errno); -#endif - close(); - return false; - } - - return true; -} - -int CTCPSocket::read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs) -{ - assert(buffer != NULL); - assert(length > 0U); - assert(m_fd != -1); - - // Check that the recv() won't block - fd_set readFds; - FD_ZERO(&readFds); -#if defined(_WIN32) || defined(_WIN64) - FD_SET((unsigned int)m_fd, &readFds); -#else - FD_SET(m_fd, &readFds); -#endif - - // Return after timeout - timeval tv; - tv.tv_sec = secs; - tv.tv_usec = msecs * 1000; - - int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); - if (ret < 0) { -#if defined(_WIN32) || defined(_WIN64) - LogError("Error returned from TCP client select, err=%d", ::GetLastError()); -#else - LogError("Error returned from TCP client select, err=%d", errno); -#endif - return -1; - } - -#if defined(_WIN32) || defined(_WIN64) - if (!FD_ISSET((unsigned int)m_fd, &readFds)) - return 0; -#else - if (!FD_ISSET(m_fd, &readFds)) - return 0; -#endif - - ssize_t len = ::recv(m_fd, (char*)buffer, length, 0); - if (len == 0) { - return -2; - } else if (len < 0) { -#if defined(_WIN32) || defined(_WIN64) - LogError("Error returned from recv, err=%d", ::GetLastError()); -#else - LogError("Error returned from recv, err=%d", errno); -#endif - return -1; - } - - return len; -} - -int CTCPSocket::readLine(std::string& line, unsigned int secs) -{ - // Maybe there is a better way to do this like reading blocks, pushing them for later calls - // Nevermind, we'll read one char at a time for the time being. - int resultCode; - int len = 0; - - line.clear(); - - char c[2U]; - c[1U] = 0x00U; - - do - { - resultCode = read((unsigned char*)c, 1U, secs); - if (resultCode == 1){ - line.append(c); - len++; - } - } while (c[0U] != '\n' && resultCode == 1); - - return resultCode <= 0 ? resultCode : len; -} - -bool CTCPSocket::write(const unsigned char* buffer, unsigned int length) -{ - assert(buffer != NULL); - assert(length > 0U); - assert(m_fd != -1); - - ssize_t ret = ::send(m_fd, (char *)buffer, length, 0); - if (ret != ssize_t(length)) { -#if defined(_WIN32) || defined(_WIN64) - LogError("Error returned from send, err=%d", ::GetLastError()); -#else - LogError("Error returned from send, err=%d", errno); -#endif - return false; - } - - return true; -} - -bool CTCPSocket::writeLine(const std::string& line) -{ - std::string lineCopy(line); - if (lineCopy.length() > 0 && lineCopy.at(lineCopy.length() - 1) != '\n') - lineCopy.append("\n"); - - //stupidly write one char after the other - size_t len = lineCopy.length(); - bool result = true; - for (size_t i = 0U; i < len && result; i++){ - unsigned char c = lineCopy.at(i); - result = write(&c , 1); - } - - return result; -} - -void CTCPSocket::close() -{ - if (m_fd != -1) { -#if defined(_WIN32) || defined(_WIN64) - ::closesocket(m_fd); -#else - ::close(m_fd); -#endif - m_fd = -1; - } -} diff --git a/NXDNGateway/TCPSocket.h b/NXDNGateway/TCPSocket.h deleted file mode 100644 index f3321ed..0000000 --- a/NXDNGateway/TCPSocket.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2010,2011,2012,2013,2016 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 TCPSocket_H -#define TCPSocket_H - -#if !defined(_WIN32) && !defined(_WIN64) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#else -#include -#endif - -#include - -class CTCPSocket { -public: - CTCPSocket(const std::string& address, unsigned int port); - ~CTCPSocket(); - - bool open(); - - int read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs = 0U); - int readLine(std::string& line, unsigned int secs); - bool write(const unsigned char* buffer, unsigned int length); - bool writeLine(const std::string& line); - - void close(); - -private: - std::string m_address; - unsigned short m_port; - int m_fd; -}; - -#endif diff --git a/NXDNGateway/UDPSocket.cpp b/NXDNGateway/UDPSocket.cpp index c105b0b..3c1451b 100644 --- a/NXDNGateway/UDPSocket.cpp +++ b/NXDNGateway/UDPSocket.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2016,2020 by Jonathan Naylor G4KLX + * Copyright (C) 2006-2016 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 @@ -261,31 +261,3 @@ 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; -} diff --git a/NXDNGateway/UDPSocket.h b/NXDNGateway/UDPSocket.h index ffc3b92..e0af272 100644 --- a/NXDNGateway/UDPSocket.h +++ b/NXDNGateway/UDPSocket.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2011,2013,2015,2016 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,8 +47,6 @@ public: void close(); - unsigned long getLocalAddress() const; - static in_addr lookup(const std::string& hostName); private: diff --git a/NXDNGateway/Version.h b/NXDNGateway/Version.h index 1c4898c..dc7a96b 100644 --- a/NXDNGateway/Version.h +++ b/NXDNGateway/Version.h @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20200519"; +const char* VERSION = "20200701"; #endif diff --git a/NXDNParrot/Makefile b/NXDNParrot/Makefile index 9a4b426..4a03a5f 100644 --- a/NXDNParrot/Makefile +++ b/NXDNParrot/Makefile @@ -14,5 +14,8 @@ NXDNParrot: $(OBJECTS) %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< +install: + install -m 755 NXDNParrot /usr/local/bin/ + clean: $(RM) NXDNParrot *.o *.d *.bak *~ diff --git a/NXDNReflector/KenwoodNetwork.cpp b/NXDNReflector/KenwoodNetwork.cpp index 979c021..134ea55 100644 --- a/NXDNReflector/KenwoodNetwork.cpp +++ b/NXDNReflector/KenwoodNetwork.cpp @@ -24,6 +24,7 @@ #include #include #include +#include const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; @@ -36,25 +37,42 @@ const unsigned int RTP_PORT = 64000U; const unsigned int RTCP_PORT = 64001U; CKenwoodNetwork::CKenwoodNetwork(const std::string& address, bool debug) : -m_rtcpSocket(RTCP_PORT), m_rtpSocket(RTP_PORT), -m_stopWatch(), +m_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_timeStamp(0U), m_ssrc(0U), m_debug(debug), -m_timer(1000U, 0U, 200U) +m_startSecs(0U), +m_startUSecs(0U), +m_rtcpTimer(1000U, 0U, 200U), +m_hangTimer(1000U, 5U), +m_hangType(0U), +m_hangSrc(0U), +m_hangDst(0U), +m_random() { assert(!address.empty()); + m_sacch = new unsigned char[10U]; + m_address = CUDPSocket::lookup(address); - ::srand((unsigned int)m_stopWatch.time()); + std::random_device rd; + std::mt19937 mt(rd()); + m_random = mt; } CKenwoodNetwork::~CKenwoodNetwork() { + delete[] m_sacch; } bool CKenwoodNetwork::open() @@ -72,7 +90,8 @@ bool CKenwoodNetwork::open() return false; } - m_ssrc = ::rand(); + std::uniform_int_distribution dist(0x00000001, 0xfffffffe); + m_ssrc = dist(m_random); return true; } @@ -107,12 +126,12 @@ bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData) 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[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]; @@ -122,13 +141,16 @@ bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData) switch (inData[5U] & 0x3FU) { case 0x01U: - m_timer.start(); - writeRTCPData(type, src, dst); + m_hangTimer.stop(); + m_rtcpTimer.start(); + writeRTCPStart(); return writeRTPVoiceHeader(outData); - case 0x08U: - m_timer.stop(); - writeRTCPData(type, src, dst); - return writeRTPVoiceTrailer(outData); + case 0x08U: { + m_hangTimer.start(); + bool ret = writeRTPVoiceTrailer(outData); + writeRTCPHang(type, src, dst); + return ret; + } default: return false; } @@ -224,23 +246,27 @@ bool CKenwoodNetwork::writeRTPVoiceHeader(const unsigned char* data) buffer[0U] = 0x80U; buffer[1U] = 0x66U; + m_seqNo++; buffer[2U] = (m_seqNo >> 8) & 0xFFU; buffer[3U] = (m_seqNo >> 0) & 0xFFU; - m_seqNo++; - m_timeStamp = (unsigned long)m_stopWatch.time(); - - buffer[4U] = (m_timeStamp >> 24) & 0xFFU; - buffer[5U] = (m_timeStamp >> 16) & 0xFFU; - buffer[6U] = (m_timeStamp >> 8) & 0xFFU; - buffer[7U] = (m_timeStamp >> 0) & 0xFFU; - m_timeStamp += 640U; + 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; @@ -267,19 +293,26 @@ bool CKenwoodNetwork::writeRTPVoiceTrailer(const unsigned char* data) buffer[0U] = 0x80U; buffer[1U] = 0x66U; + m_seqNo++; buffer[2U] = (m_seqNo >> 8) & 0xFFU; buffer[3U] = (m_seqNo >> 0) & 0xFFU; - buffer[4U] = (m_timeStamp >> 24) & 0xFFU; - buffer[5U] = (m_timeStamp >> 16) & 0xFFU; - buffer[6U] = (m_timeStamp >> 8) & 0xFFU; - buffer[7U] = (m_timeStamp >> 0) & 0xFFU; + 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; @@ -306,21 +339,26 @@ bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data) buffer[0U] = 0x80U; buffer[1U] = 0x66U; + m_seqNo++; buffer[2U] = (m_seqNo >> 8) & 0xFFU; buffer[3U] = (m_seqNo >> 0) & 0xFFU; - m_seqNo++; - buffer[4U] = (m_timeStamp >> 24) & 0xFFU; - buffer[5U] = (m_timeStamp >> 16) & 0xFFU; - buffer[6U] = (m_timeStamp >> 8) & 0xFFU; - buffer[7U] = (m_timeStamp >> 0) & 0xFFU; - m_timeStamp += 640U; + 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[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; @@ -337,30 +375,59 @@ bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data) return m_rtpSocket.write(buffer, 59U, m_address, RTP_PORT); } -bool CKenwoodNetwork::writeRTCPPing() +bool CKenwoodNetwork::writeRTCPStart() { +#if defined(_WIN32) || defined(_WIN64) + time_t now; + ::time(&now); + + m_startSecs = uint32_t(now); + + SYSTEMTIME st; + ::GetSystemTime(&st); + + m_startUSecs = st.wMilliseconds * 1000U; +#else + struct timeval tod; + ::gettimeofday(&tod, NULL); + + m_startSecs = tod.tv_sec; + m_startUSecs = tod.tv_usec; +#endif + unsigned char buffer[30U]; ::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[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[25U] = 0x01U; + + buffer[27U] = 0x0AU; if (m_debug) CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U); @@ -368,33 +435,84 @@ bool CKenwoodNetwork::writeRTCPPing() return m_rtcpSocket.write(buffer, 28U, m_address, RTCP_PORT); } -bool CKenwoodNetwork::writeRTCPData(unsigned char type, unsigned short src, unsigned short dst) +bool CKenwoodNetwork::writeRTCPPing() { - unsigned char buffer[20U]; - ::memset(buffer, 0x00U, 20U); + unsigned char buffer[30U]; + ::memset(buffer, 0x00U, 30U); - buffer[0U] = 0x8BU; + buffer[0U] = 0x8AU; buffer[1U] = 0xCCU; - - buffer[3U] = 0x04U; + 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[6U] = (m_ssrc >> 8) & 0xFFU; + buffer[7U] = (m_ssrc >> 0) & 0xFFU; buffer[8U] = 'K'; buffer[9U] = 'W'; buffer[10U] = 'N'; buffer[11U] = 'E'; - buffer[12U] = (src >> 8) & 0xFFU; - buffer[13U] = (src >> 0) & 0xFFU; + buffer[12U] = (m_startSecs >> 24) & 0xFFU; + buffer[13U] = (m_startSecs >> 16) & 0xFFU; + buffer[14U] = (m_startSecs >> 8) & 0xFFU; + buffer[15U] = (m_startSecs >> 0) & 0xFFU; - buffer[14U] = (dst >> 8) & 0xFFU; - buffer[15U] = (dst >> 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[16U] = type; + buffer[22U] = 0x02U; + + buffer[24U] = 0x01U; + + buffer[27U] = 0x7BU; + + if (m_debug) + CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U); + + return m_rtcpSocket.write(buffer, 28U, m_address, RTCP_PORT); +} + +bool CKenwoodNetwork::writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst) +{ + m_hangType = type; + m_hangSrc = src; + m_hangDst = dst; + + return writeRTCPHang(); +} + +bool CKenwoodNetwork::writeRTCPHang() +{ + unsigned char buffer[30U]; + ::memset(buffer, 0x00U, 30U); + + buffer[0U] = 0x8BU; + buffer[1U] = 0xCCU; + buffer[2U] = 0x00U; + buffer[3U] = 0x04U; + + buffer[4U] = (m_ssrc >> 24) & 0xFFU; + buffer[5U] = (m_ssrc >> 16) & 0xFFU; + buffer[6U] = (m_ssrc >> 8) & 0xFFU; + buffer[7U] = (m_ssrc >> 0) & 0xFFU; + + buffer[8U] = 'K'; + buffer[9U] = 'W'; + buffer[10U] = 'N'; + buffer[11U] = 'E'; + + buffer[12U] = (m_hangSrc >> 8) & 0xFFU; + buffer[13U] = (m_hangSrc >> 0) & 0xFFU; + + buffer[14U] = (m_hangDst >> 8) & 0xFFU; + buffer[15U] = (m_hangDst >> 0) & 0xFFU; + + buffer[16U] = m_hangType; if (m_debug) CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U); @@ -409,26 +527,23 @@ unsigned int CKenwoodNetwork::read(unsigned char* data) unsigned char dummy[BUFFER_LENGTH]; readRTCP(dummy); - bool ret; - unsigned int len = readRTP(data); - if (len > 0U) { - switch (data[9U]) { - case 0x05U: // Voice header or trailer - ret = processKenwoodVoiceHeader(data); - if (!ret) - return 0U; - return 33U; - case 0x08U: // Voice data - processKenwoodVoiceData(data); - return 33U; - default: - break; - } + 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; } - - CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len); - return 0U; } unsigned int CKenwoodNetwork::readRTP(unsigned char* data) @@ -444,19 +559,14 @@ unsigned int CKenwoodNetwork::readRTP(unsigned char* data) return 0U; // Check if the data is for us - if (m_address.s_addr != address.s_addr || port != RTP_PORT) { - LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, RTP_PORT, port); + 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); - if (length != 47 && length != 59) { - LogError("Invalid RTP length of %d", length); - return 0U; - } - ::memcpy(data, buffer + 12U, length - 12U); return length - 12U; @@ -475,19 +585,14 @@ unsigned int CKenwoodNetwork::readRTCP(unsigned char* data) return 0U; // Check if the data is for us - if (m_address.s_addr != address.s_addr || port != RTCP_PORT) { - LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, RTCP_PORT, port); + 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 (length != 20 && length != 28) { - LogError("Invalid RTCP length of %d", length); - return 0U; - } - if (::memcmp(buffer + 8U, "KWNE", 4U) != 0) { LogError("Missing RTCP KWNE signature"); return 0U; @@ -508,14 +613,23 @@ void CKenwoodNetwork::close() void CKenwoodNetwork::clock(unsigned int ms) { - m_timer.clock(ms); - if (m_timer.isRunning() && m_timer.hasExpired()) { - writeRTCPPing(); - m_timer.start(); + 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(); } } -bool CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData) +unsigned int CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData) { assert(inData != NULL); @@ -552,15 +666,27 @@ bool CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData) 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); - return true; + m_headerSeen = false; + m_seen1 = false; + m_seen2 = false; + m_seen3 = false; + m_seen4 = false; + return 33U; default: - return false; + return 0U; } } -void CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData) +unsigned int CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData) { assert(inData != NULL); @@ -642,4 +768,162 @@ void CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData) } ::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); } diff --git a/NXDNReflector/KenwoodNetwork.h b/NXDNReflector/KenwoodNetwork.h index 4c0ded9..e597802 100644 --- a/NXDNReflector/KenwoodNetwork.h +++ b/NXDNReflector/KenwoodNetwork.h @@ -20,12 +20,12 @@ #define KenwoodNetwork_H #include "CoreNetwork.h" -#include "StopWatch.h" #include "UDPSocket.h" #include "Timer.h" #include #include +#include class CKenwoodNetwork : public ICoreNetwork { public: @@ -45,25 +45,42 @@ public: private: CUDPSocket m_rtpSocket; CUDPSocket m_rtcpSocket; - CStopWatch m_stopWatch; in_addr m_address; - unsigned short m_seqNo; - unsigned long m_timeStamp; + 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; - CTimer m_timer; + uint32_t m_startSecs; + uint32_t m_startUSecs; + CTimer m_rtcpTimer; + CTimer m_hangTimer; + unsigned char m_hangType; + unsigned short m_hangSrc; + unsigned short m_hangDst; + std::mt19937 m_random; bool processIcomVoiceHeader(const unsigned char* data); bool processIcomVoiceData(const unsigned char* data); - bool processKenwoodVoiceHeader(unsigned char* data); - void processKenwoodVoiceData(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 writeRTCPData(unsigned char type, unsigned short src, unsigned short dst); + bool writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst); + bool writeRTCPHang(); unsigned int readRTP(unsigned char* data); unsigned int readRTCP(unsigned char* data); + unsigned long getTimeStamp() const; }; #endif diff --git a/NXDNReflector/Makefile b/NXDNReflector/Makefile index 43c534b..21567be 100644 --- a/NXDNReflector/Makefile +++ b/NXDNReflector/Makefile @@ -14,6 +14,9 @@ NXDNReflector: $(OBJECTS) %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< +install: + install -m 755 NXDNReflector /usr/local/bin/ + clean: $(RM) NXDNReflector *.o *.d *.bak *~ \ No newline at end of file diff --git a/NXDNReflector/UDPSocket.cpp b/NXDNReflector/UDPSocket.cpp index 396f1f7..eaa2e2f 100644 --- a/NXDNReflector/UDPSocket.cpp +++ b/NXDNReflector/UDPSocket.cpp @@ -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 diff --git a/NXDNReflector/UDPSocket.h b/NXDNReflector/UDPSocket.h index e0af272..3437b53 100644 --- a/NXDNReflector/UDPSocket.h +++ b/NXDNReflector/UDPSocket.h @@ -47,7 +47,7 @@ public: void close(); - static in_addr lookup(const std::string& hostName); + static in_addr lookup(const std::string& hostName); private: std::string m_address; diff --git a/NXDNReflector/Version.h b/NXDNReflector/Version.h index 301ae35..2cda46b 100644 --- a/NXDNReflector/Version.h +++ b/NXDNReflector/Version.h @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20200420"; +const char* VERSION = "20200701"; #endif