Merge branch 'feature/DRats' into develop

pull/32/head
Geoffrey Merck 3 years ago
commit 4283b3c78b

@ -0,0 +1,267 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA
*
* 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 "TCPReaderWriterServer.h"
#include "Log.h"
#include "NetUtils.h"
#include <cerrno>
#include <cassert>
CTCPReaderWriterServer::CTCPReaderWriterServer(const std::string& address, unsigned int port) :
CThread("TCP server"),
m_address(address),
m_port(port),
m_fd(-1),
m_client(NULL),
m_stopped(false)
{
assert(port > 0U);
}
CTCPReaderWriterServer::~CTCPReaderWriterServer()
{
}
bool CTCPReaderWriterServer::start()
{
bool ret = open();
if (!ret) {
close();
return false;
}
Create();
Run();
return true;
}
int CTCPReaderWriterServer::read(unsigned char* buffer, unsigned int length, unsigned int secs)
{
assert(buffer != NULL);
assert(length > 0U);
if (m_client != NULL) {
int ret = m_client->read(buffer, length, secs);
if (ret < 0) {
CLog::logInfo("Lost TCP connection to port %u", m_port);
m_client->close();
delete m_client;
m_client = NULL;
open();
return 0;
}
return ret;
}
return 0;
}
bool CTCPReaderWriterServer::write(const unsigned char* buffer, unsigned int length)
{
assert(buffer != NULL);
assert(length > 0U);
if (m_client != NULL) {
bool ret = m_client->write(buffer, length);
if (!ret) {
CLog::logInfo("Lost TCP connection to port %u", m_port);
m_client->close();
delete m_client;
m_client = NULL;
open();
return false;
}
return true;
}
return true;
}
void* CTCPReaderWriterServer::Entry()
{
#ifndef DEBUG_DSTARGW
try {
#endif
while (!m_stopped) {
int ret = accept();
switch (ret) {
case -2:
break;
case -1:
break;
default:
CLog::logInfo("Incoming TCP connection to port %u", m_port);
m_client = new CTCPReaderWriterClient(ret);
close();
break;
}
Sleep(1000UL);
}
if (m_client != NULL) {
m_client->close();
delete m_client;
}
close();
#ifndef DEBUG_DSTARGW
}
catch (std::exception& e) {
std::string message(e.what());
CLog::logError("Exception raised in the TCP Reader-Writer Server thread - \"%s\"", message.c_str());
}
catch (...) {
CLog::logError("Unknown exception raised in the TCP Reader-Writer Server thread");
}
#endif
return NULL;
}
void CTCPReaderWriterServer::stop()
{
m_stopped = true;
Wait();
}
bool CTCPReaderWriterServer::open()
{
m_fd = ::socket(PF_INET, SOCK_STREAM, 0);
if (m_fd < 0) {
CLog::logError("Cannot create the TCP server socket, err=%d", errno);
return false;
}
struct sockaddr_in addr;
::memset(&addr, 0x00, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(m_port);
if (m_address.empty())
addr.sin_addr.s_addr = htonl(INADDR_ANY);
else
addr.sin_addr = lookup(m_address);
if (addr.sin_addr.s_addr == INADDR_NONE) {
CLog::logError("The address is invalid - %s", m_address.c_str());
close();
return false;
}
int reuse = 1;
if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) {
CLog::logError("Cannot set the TCP server socket option, err=%d", errno);
close();
return false;
}
if (::bind(m_fd, (sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) {
CLog::logError("Cannot bind the TCP server address, err=%d", errno);
close();
return false;
}
::listen(m_fd, 5);
return true;
}
int CTCPReaderWriterServer::accept()
{
if (m_fd == -1)
return -1;
// Check that the accept() won't block
fd_set readFds;
FD_ZERO(&readFds);
#if defined(__WINDOWS__)
FD_SET((unsigned int)m_fd, &readFds);
#else
FD_SET(m_fd, &readFds);
#endif
// Return after timeout
timeval tv;
tv.tv_sec = 0L;
tv.tv_usec = 0L;
int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv);
if (ret < 0) {
CLog::logError("Error returned from TCP server select, err=%d", errno);
return -2;
}
#if defined(__WINDOWS__)
if (!FD_ISSET((unsigned int)m_fd, &readFds))
return -1;
#else
if (!FD_ISSET(m_fd, &readFds))
return -1;
#endif
struct sockaddr_in addr;
#if defined(__WINDOWS__)
int len = sizeof(struct sockaddr_in);
#else
socklen_t len = sizeof(struct sockaddr_in);
#endif
ret = ::accept(m_fd, (sockaddr*)&addr, &len);
if (ret < 0) {
CLog::logError("Error returned from TCP server accept, err=%d", errno);
}
return ret;
}
void CTCPReaderWriterServer::close()
{
if (m_fd != -1) {
::close(m_fd);
m_fd = -1;
}
}
in_addr CTCPReaderWriterServer::lookup(const std::string& hostname) const
{
in_addr addrv4;
addrv4.s_addr = INADDR_NONE;
sockaddr_storage addr;
auto res = CNetUtils::lookupV4(hostname, addr);
if(res) {
addrv4 = TOIPV4(addr)->sin_addr;
}
return addrv4;
}

@ -0,0 +1,61 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA
*
* 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.
*/
#pragma once
#include "TCPReaderWriterClient.h"
#include "Thread.h"
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string>
class CTCPReaderWriterServer : public CThread {
public:
CTCPReaderWriterServer(const std::string& address, unsigned int port);
virtual ~CTCPReaderWriterServer();
virtual bool start();
virtual bool write(const unsigned char* buffer, unsigned int length);
virtual int read(unsigned char* buffer, unsigned int length, unsigned int secs);
virtual void stop();
virtual void* Entry();
private:
std::string m_address;
unsigned short m_port;
int m_fd;
CTCPReaderWriterClient* m_client;
bool m_stopped;
bool open();
int accept();
void close();
in_addr lookup(const std::string& hostname) const;
};

@ -157,23 +157,6 @@ bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, c
TOIPV4(addr)->sin_port = htons(port);
return write(buffer, length, addr);
// sockaddr_in addr;
// ::memset(&addr, 0x00, sizeof(sockaddr_in));
// addr.sin_family = AF_INET;
// addr.sin_addr = address;
// addr.sin_port = htons(port);
// ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in));
// if (ret < 0) {
// CLog::logError("Error returned from sendto (port: %u), err: %s\n", m_port, strerror(errno));
// return false;
// }
// if (ret != ssize_t(length))
// return false;
// return true;
}
bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, const struct sockaddr_storage& addr)

@ -0,0 +1,384 @@
/*
* Copyright (C) 2011-2015 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 "DStarDefines.h"
#include "DRATSServer.h"
#include "Utils.h"
#include "Log.h"
#include <cassert>
#include <string.h>
#include <chrono>
// #define LOOPBACK
const unsigned int BUFFER_LENGTH = 30000U;
CDRATSServer::CDRATSServer(const std::string& address, unsigned int port, const std::string& callsign, IRepeaterCallback* handler) :
CThread("DRats"),
m_address(address),
m_port(port),
m_callsign(callsign),
m_handler(handler),
m_socket(NULL),
m_stopped(false),
m_readState(SS_FIRST),
m_readBuffer(NULL),
m_readLength(0U),
m_readPos(0U),
m_readEnd(false),
m_writeText(NULL),
m_writeState(SS_FIRST),
m_writeBuffer(NULL),
m_writeLength(0U)
{
assert(handler != NULL);
assert(port > 0U);
m_readBuffer = new unsigned char[BUFFER_LENGTH];
m_writeBuffer = new unsigned char[BUFFER_LENGTH];
m_writeText = new unsigned char[6U];
}
CDRATSServer::~CDRATSServer()
{
delete[] m_readBuffer;
delete[] m_writeBuffer;
delete[] m_writeText;
}
bool CDRATSServer::open()
{
m_socket = new CTCPReaderWriterServer(m_address, m_port);
bool ret = m_socket->start();
if (!ret) {
delete m_socket;
m_socket = NULL;
return false;
}
Create();
Run();
return true;
}
void CDRATSServer::writeHeader(const CHeaderData&)
{
m_writeState = SS_FIRST;
if (m_writeLength > 0U && m_socket != NULL) {
CUtils::dump("From RF", m_writeBuffer, m_writeLength);
m_socket->write(m_writeBuffer, m_writeLength);
}
m_writeLength = 0U;
}
void CDRATSServer::writeData(const CAMBEData& data)
{
// Sync data isn't sent on
if (data.isSync()) {
m_writeState = SS_FIRST;
return;
}
if (data.isEnd()) {
if (m_writeLength > 0U && m_socket != NULL) {
CUtils::dump("From RF", m_writeBuffer, m_writeLength);
m_socket->write(m_writeBuffer, m_writeLength);
}
m_writeLength = 0U;
return;
}
unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES];
unsigned int length = data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES);
if (length != DV_FRAME_LENGTH_BYTES)
return;
unsigned char byte1 = buffer[VOICE_FRAME_LENGTH_BYTES + 0U] ^ SCRAMBLER_BYTE1;
unsigned char byte2 = buffer[VOICE_FRAME_LENGTH_BYTES + 1U] ^ SCRAMBLER_BYTE2;
unsigned char byte3 = buffer[VOICE_FRAME_LENGTH_BYTES + 2U] ^ SCRAMBLER_BYTE3;
switch (m_writeState) {
case SS_FIRST:
m_writeText[0U] = byte1;
m_writeText[1U] = byte2;
m_writeText[2U] = byte3;
m_writeState = SS_SECOND;
return;
case SS_SECOND:
m_writeText[3U] = byte1;
m_writeText[4U] = byte2;
m_writeText[5U] = byte3;
m_writeState = SS_FIRST;
break;
}
if ((m_writeText[0U] & SLOW_DATA_TYPE_MASK) != SLOW_DATA_TYPE_GPS)
return;
length = m_writeText[0U] & 0x07; // Maximum value of 5
if (length > 5U)
length = 5U;
for (unsigned int i = 0U; i < length; i++) {
m_writeBuffer[m_writeLength++] = m_writeText[i + 1U];
// Check for [EOB] in the buffer to signal the end of the D-RATS data.
// To allow strstr() to run correctly
m_writeBuffer[m_writeLength] = 0x00U;
if (::strstr((char*)m_writeBuffer, "[EOB]") != NULL) {
if (m_socket != NULL) {
CUtils::dump("From RF", m_writeBuffer, m_writeLength);
m_socket->write(m_writeBuffer, m_writeLength);
}
m_writeLength = 0U;
}
}
}
void CDRATSServer::writeEnd()
{
if (m_writeLength > 0U && m_socket != NULL) {
CUtils::dump("From RF", m_writeBuffer, m_writeLength);
m_socket->write(m_writeBuffer, m_writeLength);
}
m_writeLength = 0U;
}
void* CDRATSServer::Entry()
{
CLog::logInfo("Starting the D-RATS Server thread for %s", m_callsign.c_str());
bool sending = false;
unsigned int id = 0U;
unsigned char seqNo = 0U;
unsigned int sent = 0U;
std::chrono::high_resolution_clock::time_point time;
#ifndef DEBUG_DSTARGW
try {
#endif
while (!m_stopped) {
serviceSocket();
if (m_readEnd && !sending) {
id = CHeaderData::createId();
// Write header
CHeaderData header;
header.setMyCall1(m_callsign);
header.setMyCall2("DATA");
header.setYourCall("CQCQCQ ");
header.setId(id);
#if defined(LOOPBACK)
writeHeader(header);
#else
m_handler->process(header, DIR_INCOMING, AS_DRATS);
#endif
m_readState = SS_FIRST;
m_readPos = 0U;
sending = true;
seqNo = 0U;
sent = 0U;
time = std::chrono::high_resolution_clock::now();
}
if (m_readEnd && sending) {
unsigned int needed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - time).count() / DSTAR_FRAME_TIME_MS;
while (sent < needed && sending) {
// Write AMBE data
CAMBEData data;
data.setId(id);
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
// Insert sync bytes when the sequence number is zero, slow data otherwise
if (seqNo == 0U) {
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
m_readState = SS_FIRST;
} else {
if (m_readState == SS_FIRST) {
unsigned char readText[3U];
::memset(readText, 'f', 3U);
unsigned int length = m_readLength - m_readPos;
unsigned char bytes = 5U;
if (length < 5U)
bytes = length;
readText[0U] = SLOW_DATA_TYPE_GPS | bytes;
for (unsigned int i = 0U; i < 2U && m_readPos < m_readLength; i++)
readText[i + 1U] = m_readBuffer[m_readPos++];
readText[0U] ^= SCRAMBLER_BYTE1;
readText[1U] ^= SCRAMBLER_BYTE2;
readText[2U] ^= SCRAMBLER_BYTE3;
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES);
m_readState = SS_SECOND;
} else {
unsigned char readText[3U];
::memset(readText, 'f', 3U);
for (unsigned int i = 0U; i < 3U && m_readPos < m_readLength; i++)
readText[i] = m_readBuffer[m_readPos++];
readText[0U] ^= SCRAMBLER_BYTE1;
readText[1U] ^= SCRAMBLER_BYTE2;
readText[2U] ^= SCRAMBLER_BYTE3;
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES);
m_readState = SS_FIRST;
}
}
data.setSeq(seqNo);
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
sent++;
#if defined(LOOPBACK)
writeData(data);
#else
m_handler->process(data, DIR_INCOMING, AS_DRATS);
#endif
if (m_readPos == m_readLength) {
if (m_readState == SS_SECOND) {
seqNo++;
if (seqNo == 21U)
seqNo = 0U;
unsigned char readText[3U];
readText[0U] = 'f' ^ SCRAMBLER_BYTE1;
readText[1U] = 'f' ^ SCRAMBLER_BYTE2;
readText[2U] = 'f' ^ SCRAMBLER_BYTE3;
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES);
data.setSeq(seqNo);
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
sent++;
#if defined(LOOPBACK)
writeData(data);
#else
m_handler->process(data, DIR_INCOMING, AS_DRATS);
#endif
}
seqNo++;
if (seqNo == 21U)
seqNo = 0U;
if (seqNo == 0U)
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
else
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, NULL_SLOW_DATA_BYTES, DATA_FRAME_LENGTH_BYTES);
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
data.setSeq(seqNo);
data.setEnd(true);
sent++;
#if defined(LOOPBACK)
writeData(data);
#else
m_handler->process(data, DIR_INCOMING, AS_DRATS);
#endif
m_readLength = 0U;
m_readPos = 0U;
m_readEnd = false;
sending = false;
sent = 0U;
}
seqNo++;
if (seqNo == 21U)
seqNo = 0U;
}
}
// 50ms
Sleep(50UL);
}
if (m_socket != NULL)
m_socket->stop();
#ifndef DEBUG_DSTARGW
}
catch (std::exception& e) {
std::string message(e.what());
CLog::logError("Exception raised in the D-RATS Server thread - \"%s\""), message.c_str();
}
catch (...) {
CLog::logError("Unknown exception raised in the D-RATS Server thread");
}
#endif
CLog::logInfo("Stopping the D-RATS Server thread for %s", m_callsign.c_str());
return NULL;
}
void CDRATSServer::close()
{
m_stopped = true;
Wait();
}
void CDRATSServer::serviceSocket()
{
if (m_socket == NULL) {
m_readLength = 0U;
m_readPos = 0U;
m_readEnd = false;
return;
}
int len = m_socket->read(m_readBuffer + m_readLength, BUFFER_LENGTH - m_readLength, 0U);
if (len > 0) {
m_readLength += len;
if (!m_readEnd) {
// To allow strstr() to run correctly
m_readBuffer[m_readLength] = 0x00U;
if (::strstr((char*)m_readBuffer, "[EOB]") != NULL) {
CUtils::dump("To RF", m_readBuffer, m_readLength);
m_readEnd = true;
}
}
}
}

@ -0,0 +1,66 @@
/*
* Copyright (C) 2011,2012 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 DRATSServer_H
#define DRATSServer_H
#include "TCPReaderWriterServer.h"
#include "RepeaterCallback.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Defs.h"
#include "Thread.h"
#include <string>
class CDRATSServer : public CThread {
public:
CDRATSServer(const std::string& address, unsigned int port, const std::string& callsign, IRepeaterCallback* handler);
virtual ~CDRATSServer();
virtual bool open();
virtual void writeHeader(const CHeaderData& header);
virtual void writeData(const CAMBEData& data);
virtual void writeEnd();
virtual void close();
virtual void* Entry();
private:
std::string m_address;
unsigned int m_port;
std::string m_callsign;
IRepeaterCallback* m_handler;
CTCPReaderWriterServer* m_socket;
bool m_stopped;
SLOWDATA_STATE m_readState;
unsigned char* m_readBuffer;
unsigned int m_readLength;
unsigned int m_readPos;
bool m_readEnd;
unsigned char* m_writeText;
SLOWDATA_STATE m_writeState;
unsigned char* m_writeBuffer;
unsigned int m_writeLength;
void serviceSocket();
};
#endif

@ -63,11 +63,8 @@ CAPRSHandler* CRepeaterHandler::m_aprsWriter = NULL;
CCallsignList* CRepeaterHandler::m_restrictList = NULL;
#ifdef USE_DRATS
CRepeaterHandler::CRepeaterHandler(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unigned char band3) :
#else
CRepeaterHandler::CRepeaterHandler(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3) :
#endif
CRepeaterHandler::CRepeaterHandler(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3) :
m_index(0x00U),
m_rptCallsign(),
m_gwyCallsign(),
@ -137,9 +134,7 @@ m_wxAudio(NULL),
m_wxNeeded(false),
#endif
m_version(NULL),
#ifdef USE_DRATS
m_drats(NULL),
#endif
m_dtmf(),
m_pollTimer(1000U, 900U), // 15 minutes
#ifdef USE_CSS
@ -228,7 +223,7 @@ m_heardTimer(1000U, 0U, 100U) // 100ms
m_version = new CVersionUnit(this, callsign);
m_aprsUnit = new CAPRSUnit(this);
#ifdef USE_DRATS
if (dratsEnabled) {
m_drats = new CDRATSServer(m_localAddress, port, callsign, this);
bool ret = m_drats->open();
@ -237,7 +232,6 @@ m_heardTimer(1000U, 0U, 100U) // 100ms
m_drats = NULL;
}
}
#endif
}
CRepeaterHandler::~CRepeaterHandler()
@ -250,10 +244,8 @@ CRepeaterHandler::~CRepeaterHandler()
#endif
delete m_version;
#ifdef USE_DRATS
if (m_drats != NULL)
m_drats->close();
#endif
}
void CRepeaterHandler::initialise(unsigned int maxRepeaters)
@ -272,21 +264,14 @@ void CRepeaterHandler::setIndex(unsigned int index)
m_index = index;
}
#ifdef USE_DRATS
void CRepeaterHandler::add(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3)
#else
void CRepeaterHandler::add(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3)
#endif
{
assert(!callsign.empty());
assert(port > 0U);
assert(handler != NULL);
#ifdef USE_DRATS
CRepeaterHandler* repeater = new CRepeaterHandler(callsign, band, address, port, hwType, reflector, atStartup, reconnect, dratsEnabled, frequency, offset, range, latitude, longitude, agl, description1, description2, url, handler, band1, band2, band3);
#else
CRepeaterHandler* repeater = new CRepeaterHandler(callsign, band, address, port, hwType, reflector, atStartup, reconnect, frequency, offset, range, latitude, longitude, agl, description1, description2, url, handler, band1, band2, band3);
#endif
for (unsigned int i = 0U; i < m_maxRepeaters; i++) {
if (m_repeaters[i] == NULL) {
@ -606,10 +591,8 @@ void CRepeaterHandler::processRepeater(CHeaderData& header)
// The Icom heard timer
m_heardTimer.stop();
#ifdef USE_DRATS
if (m_drats != NULL)
m_drats->writeHeader(header);
#endif
// Reset the statistics
m_frames = 0U;
@ -836,10 +819,9 @@ void CRepeaterHandler::processRepeater(CAMBEData& data)
// CCS gets everything
m_ccsHandler->writeAMBE(data);
#endif
#ifdef USE_DRATS
if (m_drats != NULL)
m_drats->writeData(data);
#endif
if (m_aprsWriter != NULL)
m_aprsWriter->writeData(m_rptCallsign, data);
@ -1572,10 +1554,10 @@ void CRepeaterHandler::clockInt(unsigned int ms)
if (m_repeaterId != 0x00U) {
if (m_text.empty())
sendHeard();
#ifdef USE_DRATS
if (m_drats != NULL)
m_drats->writeEnd();
#endif
sendStats();

@ -20,10 +20,6 @@
#ifndef RepeaterHandler_H
#define RepeaterHandler_H
//#define USE_CCS
//#define USE_STARNET
//#define USE_DRATS
#include "RepeaterProtocolHandler.h"
#include "DExtraProtocolHandler.h"
#include "DPlusProtocolHandler.h"
@ -39,9 +35,7 @@
#include "CacheManager.h"
#include "HeaderLogger.h"
#include "CallsignList.h"
#ifdef USE_DRATS
#include "DRATSServer.h"
#endif
#include "CCSCallback.h"
#include "VersionUnit.h"
#ifdef USE_CCS
@ -68,11 +62,7 @@ class CRepeaterHandler : public IRepeaterCallback, public IReflectorCallback, pu
public:
static void initialise(unsigned int maxRepeaters);
#ifdef USE_DRATS
static void add(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3);
#else
static void add(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3);
#endif
static void setLocalAddress(const std::string& address);
static void setG2HandlerPool(CG2ProtocolHandlerPool* handler);
@ -147,11 +137,8 @@ public:
virtual void readAPRSFrame(CAPRSFrame& frame);
protected:
#ifdef USE_DRATS
CRepeaterHandler(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3);
#else
CRepeaterHandler(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3);
#endif
virtual ~CRepeaterHandler();
void resolveUserInt(const std::string& user, const std::string& repeater, const std::string& gateway, const std::string& address);
@ -282,10 +269,8 @@ private:
// APRS to DPRS
CAPRSUnit* m_aprsUnit;
#ifdef USE_DRATS
// D-RATS handler
CDRATSServer* m_drats;
#endif
// DTMF commands
CDTMF m_dtmf;

@ -240,6 +240,10 @@ bool CDStarGatewayApp::createThread()
delete restrictList;
}
// Drats
TDRats drats;
m_config->getDRats(drats);
// Setup the repeaters
bool ddEnabled = false;
bool atLeastOneRepeater = false;
@ -260,6 +264,7 @@ bool CDStarGatewayApp::createThread()
rptrConfig.reflectorAtStartup,
rptrConfig.reflectorReconnect,
rptrConfig.frequency,
drats.enabled,
rptrConfig.offset,
rptrConfig.range,
rptrConfig.latitude,

@ -57,6 +57,7 @@ bool CDStarGatewayConfig::load()
#endif
ret = loadDaemon(cfg) && ret;
ret = loadAccessControl(cfg) && ret;
ret = loadDRats(cfg) && ret;
}
if(ret) {
@ -360,6 +361,13 @@ bool CDStarGatewayConfig::loadAccessControl(const CConfig & cfg)
return ret;
}
bool CDStarGatewayConfig::loadDRats(const CConfig & cfg)
{
bool ret = cfg.getValue("DRats", "enabled", m_drats.enabled, false);
return ret;
}
bool CDStarGatewayConfig::open(CConfig & cfg)
{
try {
@ -466,3 +474,8 @@ void CDStarGatewayConfig::getAccessControl(TAccessControl & accessControl) const
{
accessControl = m_accessControl;
}
void CDStarGatewayConfig::getDRats(TDRats & drats) const
{
drats = m_drats;
}

@ -56,9 +56,7 @@ typedef struct {
HW_TYPE hwType;
bool reflectorAtStartup;
RECONNECT reflectorReconnect;
#ifdef USE_DRATS
bool dRatsEnabled;
#endif
double frequency;
double offset;
double range;
@ -115,6 +113,10 @@ typedef struct {
bool enabled;
} TDCS;
typedef struct {
bool enabled;
} TDRats;
typedef struct {
bool enabled;
std::string url;
@ -164,6 +166,7 @@ public:
#endif
void getDaemon(TDaemon & gen) const;
void getAccessControl(TAccessControl & accessControl) const;
void getDRats(TDRats & drats) const;
private:
bool open(CConfig & cfg);
@ -183,6 +186,7 @@ private:
#endif
bool loadDaemon(const CConfig & cfg);
bool loadAccessControl(const CConfig & cfg);
bool loadDRats(const CConfig & cfg);
std::string m_fileName;
TGateway m_gateway;
@ -199,6 +203,7 @@ private:
#endif
TDaemon m_daemon;
TAccessControl m_accessControl;
TDRats m_drats;
std::vector<TRepeater *> m_repeaters;
std::vector<TircDDB *> m_ircDDB;

@ -511,15 +511,10 @@ void CDStarGatewayThread::setGateway(GATEWAY_TYPE gatewayType, const std::string
m_gatewayAddress = gatewayAddress;
}
#ifdef USE_DRATS
void CDStarGatewayThread::addRepeater(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3)
{
CRepeaterHandler::add(callsign, band, address, port, hwType, reflector, atStartup, reconnect, dratsEnabled, frequency, offset, range, latitude, longitude, agl, description1, description2, url, handler, band1, band2, band3);
#else
void CDStarGatewayThread::addRepeater(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3)
{
CRepeaterHandler::add(callsign, band, address, port, hwType, reflector, atStartup, reconnect, frequency, offset, range, latitude, longitude, agl, description1, description2, url, handler, band1, band2, band3);
#endif
std::string repeater = callsign;
repeater.resize(LONG_CALLSIGN_LENGTH - 1U);

@ -44,11 +44,8 @@ public:
virtual ~CDStarGatewayThread();
virtual void setGateway(GATEWAY_TYPE type, const std::string& callsign, const std::string& address);
#ifdef USE_DRATS
virtual void addRepeater(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1 = 0x00U, unsigned char band2 = 0x00U, unsigned char band3 = 0x00U);
#else
virtual void addRepeater(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1 = 0x00U, unsigned char band2 = 0x00U, unsigned char band3 = 0x00U);
#endif
#ifdef USE_STARNET
#if defined(DEXTRA_LINK) || defined(DCS_LINK)
virtual void addStarNet(const std::string& callsign, const std::string& logoff, const std::string& repeater, const std::string& infoText, const std::string& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const std::string& reflector);

@ -172,6 +172,9 @@ enabled=true # There is no reason to disable this
enabled=true
hostfileUrl=http://xlxapi.rlx.lu/api.php?do=GetXLXDMRMaster
[DRats]
enabled=false # Defaults to false. The program need to be compiled with DRats support for DRats to be actually enabled
[Remote]
enabled=false
port=4242

@ -38,7 +38,6 @@ export CPPFLAGS+= -DUSE_GPSD
export LDFLAGS+= -lgps
endif
.PHONY: all
all: DStarGateway/dstargateway DGWRemoteControl/dgwremotecontrol DGWTextTransmit/dgwtexttransmit DGWTimeServer/dgwtimeserver DGWVoiceTransmit/dgwvoicetransmit #tests

@ -5,7 +5,7 @@
- [2.3. Thanks](#23-thanks)
- [2.4. Features](#24-features)
- [2.4.1. Features that where left out :](#241-features-that-where-left-out-)
- [2.4.2. Additional Features :](#242-additional-features-)
- [2.4.2. Additional Features compared to ircddbGateway:](#242-additional-features-compared-to-ircddbgateway)
- [3. Building and installing](#3-building-and-installing)
- [3.1. Initial setup](#31-initial-setup)
- [3.2. Get latest stable version (recommended)](#32-get-latest-stable-version-recommended)
@ -16,6 +16,7 @@
- [3.5.0.2. Debug Build](#3502-debug-build)
- [3.6. Installing](#36-installing)
- [3.7. Configuring](#37-configuring)
- [3.8. Updating host files](#38-updating-host-files)
- [4. Contributing](#4-contributing)
- [4.1. Work Flow](#41-work-flow)
- [4.2. Continuous Integration](#42-continuous-integration)
@ -50,16 +51,15 @@ Quite a few classes are more or less copy/paste from each other some sanitizatio
All the features found in ircddbGateway are supposed to be working. Except the ones listed below
### 2.4.1. Features that where left out :
- CCS: is still being used? I always considered this as trojan horse to push some DMR Agenda into DStar an more or les a burdain to use. Call sign routing is by far more flexible and superior.
- CCS: is still being used? I always considered this as trojan horse to push some DMR Agenda into DStar and a burdain to use. Call sign routing is by far more flexible and superior.
- Starnet: You might consider running [Smart Group Server XL](https://github.com/F4FXL/smart-group-server-xl) from a dedicated computer instead.
- Announcement: same can be achieved using VoiceTransmit.
- APRSGateway capability: I would prefer to have some sort of TCP "APRS-IS proxy" program sitting between the program and the APRS server, thus keeping the ability to directly connect to APRS-IS or not, depending on the system owner wish. I run mostly DStar Only repeaters, having an additional program to maintain is unnecessary burden.
- DRats : Is on the to-do list see [#6](#6)
- APRSGateway capability: I would prefer to have some sort of TCP "APRS-IS proxy" program sitting between the program and the APRS server, thus keeping the ability to directly connect to APRS-IS or not, depending on the system owner wish. I run mostly DStar Only repeaters, having an additional program to maintain is unnecessary overkill.
- CallSign Server : this is a legacy from the dead project xreflector.net, I will most probably drop it for good.
### 2.4.2. Additional Features :
- DPlus, DExtra and G2 NAT Traversal using ircddb network as rendez-vous server
- Forward RSMS1A app messages from/to APRS-IS Network, yes you can send/receive messages to and from aprs.
### 2.4.2. Additional Features compared to ircddbGateway:
- DPlus, DExtra and G2 NAT Traversal using ircddb network as rendez-vous server. I.e. it is not required to open firewall ports for Callsign Routing or Gateway calls. however it is still recommended to do so. But NAT Traversal will bring more flexibility when operating on CGNAT (Mobile) Networks.
- Forward RSMS1A app messages from/to APRS-IS Network, yes you can send/receive messages to and from aprs. Yes, you can send messages to APRS stations and Vice Versa. Additionnally part of the message is sent as Text Dat in the slow data. This allows you to read the message dirdclty on your radio screen.
- Repeater Link status is sent to APRS-IS as a status frame
# 3. Building and installing
@ -104,7 +104,7 @@ make USE_GPS=1
```
make ENABLE_DEBUG=1
```
Note that this will link with libl
Note that this will will add libl dependency. Building this way will output the stack trace in case of a crash.
## 3.6. Installing
The program is meant to run as a systemd service. All bits an pieces are provided.
```
@ -116,13 +116,21 @@ After installing you have to edit the configuration file. If you went with defau
The configuration format is quite straight forward. It is organised in sections and key/value pairs.
The order of the sections or key/values pairs inside the sections does not matter nor does casing.
Boolean values can be set using true, false, 1 or 0
Floating point values must use . (point) as decimal separatorsensitive.
Floating point values must use . (point) as decimal separator.
When done with configuration, the daemon will be started automatically on boot. To manual start and stop it use the usual systemd commands
When done with configuration, the daemon will be started automatically on next boot. To manual start and stop it, use the usual systemd commands
```
sudo systemctl start dstargateway.service
sudo systemctl stop dstargateway.service
```
## 3.8. Updating host files
To update host files, from within the source code directory, run
```
sudo make newhostfiles
sudo systemctl restart dstargateway.service
```
# 4. Contributing
## 4.1. Work Flow
I Use [Git flow](https://danielkummer.github.io/git-flow-cheatsheet/) as my workflow. PR are welcome but pleasee observe following rules :
@ -131,10 +139,11 @@ I Use [Git flow](https://danielkummer.github.io/git-flow-cheatsheet/) as my work
- Code formating rules are observed (these are very lousy though)
## 4.2. Continuous Integration
I have added some basic CI using CircleCI [![F4FXL](https://circleci.com/gh/F4FXL/DStarGateway.svg?style=svg)](https://app.circleci.com/pipelines/github/F4FXL/DStarGateway?filter=all) I am trying to rewrite the code so that it can be put into some Behavior Driven Development scheme. This is a long haul task and I'll try do do it on the go while changing/adding stuff.
the testing framwework used is Google Test.
The testing framwework used is Google Test.
# 5. Version History
## 5.1. Version 0.6
- [**Improvement**] Add DRats Support ([#22](https://github.com/F4FXL/DStarGateway/issues/22))
- [**Improvement**] Add call sign lists ([#22](https://github.com/F4FXL/DStarGateway/issues/22))
- [**Improvement**] Add a way to override Slow Data in VoiceTransmit ([#23](https://github.com/F4FXL/DStarGateway/issues/23))
- [**Improvement**] Add time server
@ -167,9 +176,9 @@ I started this during my 2021 seasons holiday. It took me almost 8 days to get t
- No banging on every gateway: use ircDDB (or something else) as mitigation server to notify peer
- Support for all protocols (G2, DExtra, DPlus) DCS does nto make sense as it was historically never used as protocol for linking repeaters
- &#9745; Send the connection status to APRS-IS as a status frame
- &#9746; Reinstantiate DRATS
- &#9745; Reinstantiate DRATS
- &#9745; Migrate all the "accessories" (VoiceTransmit, RemoteControl ...)
- &#9746; Automatic refresh of host files
- &#9746; Reduce ircDDB dependency, build something more P2P, maybe based on [Distributed Hashtable](https://github.com/DavidKeller/kademlia) ?
- &#9746; Forward messages from RS-MS1A to APRS and vice versa
- &#9745; Forward messages from RS-MS1A to APRS and vice versa
- Everything that might come handy to make dstar the most powerful system ever :)

Loading…
Cancel
Save

Powered by TurnKey Linux.