Merge branch 'release/0.5'

master v0.5
Geoffrey Merck 4 years ago
commit cfbca814e3

@ -0,0 +1,33 @@
# Use the latest 2.1 version of CircleCI pipeline process engine.
# See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
# Define a job to be invoked later in a workflow.
# See: https://circleci.com/docs/2.0/configuration-reference/#jobs
jobs:
build-dstargateway:
# Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub.
# See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor
docker:
- image: cimg/base:stable
# Add steps to the job
# See: https://circleci.com/docs/2.0/configuration-reference/#steps
steps:
- checkout
- run:
name: Install dependencies
command: |
sudo apt-get update
sudo apt-get -y install libgtest-dev libcurl4-openssl-dev libboost-dev libgps-dev
- run:
name: "Build"
command: "make -j 3 ENABLE_DEBUG=1 USE_GPSD=1"
- run:
name: "Run Tests"
command: "make run-tests ENABLE_DEBUG=1 USE_GPSD=1"
# Invoke jobs via workflows
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
workflows:
dstar-gateway-workflow:
jobs:
- build-dstargateway

3
.gitignore vendored

@ -44,4 +44,5 @@ Sandbox/*
*.out *.out
*.app *.app
dstargateway dstargateway
Tests/dstargateway_tests
DGWRemoteControl/dgwremotecontrol

@ -0,0 +1,25 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/BaseCommon/",
"${workspaceFolder}/APRS/",
"${workspaceFolder}/Common/",
"${workspaceFolder}/DStarBase/",
"${workspaceFolder}/IRCDDB/",
"${workspaceFolder}/VersionInfo/",
"${workspaceFolder}/BaseCommon/",
"${workspaceFolder}/BaseCommon"
],
"defines": [],
"compilerPath": "/bin/g++",
"cStandard": "gnu17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64",
"configurationProvider": "ms-vscode.makefile-tools"
}
],
"version": 4
}

@ -5,11 +5,59 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "(gdb) Lancer", "name": "(gdb) dstargateway",
"type": "cppdbg", "type": "cppdbg",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/dstargateway", "program": "${workspaceFolder}/DStarGateway/dstargateway",
"args": ["${workspaceFolder}/___test.cfg"], "args": ["${workspaceFolder}/Sandbox/___test.cfg"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Activer l'impression en mode Pretty pour gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Définir la version désassemblage sur Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) dgwremotecontrol",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/DGWRemoteControl/dgwremotecontrol",
"args": ["--name", "city_center", "F4FXL B", "link", "never", "DCS208_C"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Activer l'impression en mode Pretty pour gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Définir la version désassemblage sur Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "Tests",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/Tests/dstargateway_tests",
"args": [ ],
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"environment": [], "environment": [],

@ -5,5 +5,100 @@
"binaryPath": "/home/geoffrey/Documents/Dev/DStarGateway/dstargateway", "binaryPath": "/home/geoffrey/Documents/Dev/DStarGateway/dstargateway",
"binaryArgs": [] "binaryArgs": []
} }
] ],
"files.associations": {
"new": "cpp",
"type_traits": "cpp",
"array": "cpp",
"*.tcc": "cpp",
"chrono": "cpp",
"deque": "cpp",
"list": "cpp",
"vector": "cpp",
"optional": "cpp",
"stop_token": "cpp",
"any": "cpp",
"bitset": "cpp",
"memory": "cpp",
"random": "cpp",
"future": "cpp",
"memory_resource": "cpp",
"ranges": "cpp",
"mutex": "cpp",
"condition_variable": "cpp",
"forward_list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"algorithm": "cpp",
"cctype": "cpp",
"cstddef": "cpp",
"cstdlib": "cpp",
"cwchar": "cpp",
"atomic": "cpp",
"bit": "cpp",
"compare": "cpp",
"cstdint": "cpp",
"exception": "cpp",
"functional": "cpp",
"iterator": "cpp",
"string_view": "cpp",
"tuple": "cpp",
"utility": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"limits": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"typeinfo": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"complex": "cpp",
"concepts": "cpp",
"cstdio": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwctype": "cpp",
"istream": "cpp",
"numbers": "cpp",
"ratio": "cpp",
"semaphore": "cpp",
"streambuf": "cpp",
"system_error": "cpp",
"thread": "cpp",
"typeindex": "cpp",
"variant": "cpp",
"iostream": "cpp",
"fstream": "cpp"
},
"editor.tokenColorCustomizations": {
"textMateRules": [
{
"scope": "googletest.failed",
"settings": {
"foreground": "#f00"
}
},
{
"scope": "googletest.passed",
"settings": {
"foreground": "#0f0"
}
},
{
"scope": "googletest.run",
"settings": {
"foreground": "#0f0"
}
}
]
},
"gtest-adapter.debugConfig": [
"Tests"
],
"gtest-adapter.supportLocation": true
} }

62
.vscode/tasks.json vendored

@ -0,0 +1,62 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build All",
"type": "shell",
"command": "make",
"args": [
"-j3",
"ENABLE_DEBUG=1",
"USE_GPSD=1",
"all"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "Build DStarGateway",
"type": "shell",
"command": "make",
"args": [
"-j3",
"ENABLE_DEBUG=1",
"USE_GPSD=1",
"DStarGateway/dstargateway"
],
"group": "build",
"problemMatcher": []
},
{
"label": "Build DGWRemoteControl",
"type": "shell",
"command": "make",
"args": [
"-j3",
"ENABLE_DEBUG=1",
"USE_GPSD=1",
"DGWRemoteControl/dgwremotecontrol"
],
"group": "build",
"problemMatcher": []
},
{
"label": "Build Tests",
"type": "shell",
"command": "make",
"args": [
"-j3",
"tests",
"ENABLE_DEBUG=1",
"USE_GPSD=1"
],
"group": "build",
"problemMatcher": []
}
]
}

@ -0,0 +1,47 @@
/*
* 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 <boost/algorithm/string.hpp>
#include "APRSFormater.h"
#include "Log.h"
#include "StringUtils.h"
bool CAPRSFormater::frameToString(std::string& output, CAPRSFrame& frame)
{
// make sur we have the minimal stuff to build a correct aprs string
if(frame.getSource().empty()
|| frame.getDestination().empty()
|| frame.getBody().empty()) {
CLog::logWarning("Invalid APRS frame, missing source, destination or body");
return false;
}
auto path = boost::join_if(frame.getPath(), ",", [](std::string s) { return !string_is_blank_or_empty(s); });
CStringUtils::string_format_in_place(output, "%s>%s%s%s:%s",
frame.getSource().c_str(),
frame.getDestination().c_str(),
path.empty() ? "" : ",",
path.c_str(),
frame.getBody().c_str());
return true;
}

@ -0,0 +1,27 @@
/*
* 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 "APRSFrame.h"
class CAPRSFormater
{
public:
static bool frameToString(std::string& output, CAPRSFrame& frame);
};

@ -0,0 +1,53 @@
/*
* 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 "APRSFrame.h"
CAPRSFrame::CAPRSFrame() :
m_source(),
m_destination(),
m_path(),
m_body(),
m_type(APFT_UNKNOWN)
{
}
CAPRSFrame::CAPRSFrame(const std::string& source, const std::string& destination, const std::vector<std::string>& path, const std::string& body, APRS_FRAME_TYPE type) :
m_source(source),
m_destination(destination),
m_path(),
m_body(body),
m_type(type)
{
m_path.assign(path.begin(), path.end());
}
CAPRSFrame::~CAPRSFrame()
{
m_path.clear();
}
void CAPRSFrame::clear()
{
m_source.clear();
m_destination.clear();
m_path.clear();
m_body.clear();
m_type = APFT_UNKNOWN;
}

@ -0,0 +1,55 @@
/*
* 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 <string>
#include <vector>
// We only support these types for now
enum APRS_FRAME_TYPE {
APFT_UNKNOWN = 0,
APFT_MESSAGE,
APFT_POSITION,
APFT_NMEA,
APFT_STATUS,
APFT_OBJECT,
APFT_WX,
APFT_TELEMETRY
};
class CAPRSFrame {
public:
CAPRSFrame();
CAPRSFrame(const std::string& source, const std::string& destination, const std::vector<std::string>& path, const std::string& body, APRS_FRAME_TYPE type);
~CAPRSFrame();
void clear();
std::string& getSource() { return m_source; }
std::string& getDestination() { return m_destination; }
std::vector<std::string>& getPath() { return m_path; }
std::string& getBody() { return m_body; }
APRS_FRAME_TYPE& getType() { return m_type; }
private:
std::string m_source;
std::string m_destination;
std::vector<std::string> m_path;
std::string m_body;
APRS_FRAME_TYPE m_type;
};

@ -0,0 +1,160 @@
/*
* 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 "APRSParser.h"
#include "Log.h"
bool CAPRSParser::parseFrame(const std::string& frameStr, CAPRSFrame& frame)
{
frame.clear();
bool ret = false;
if(!frameStr.empty()) {
auto pos = frameStr.find_first_of(':');
if(pos != std::string::npos && pos != frameStr.length() - 1) {
auto header = frameStr.substr(0, pos); // contains source, dest and path
auto body = frameStr.substr(pos +1);
std::vector<std::string> headerSplits;
boost::split(headerSplits, header, [](char c) { return c == ',' || c == '>';});
//we need at least source and dest to form a valid frame, also headers shall not contain empty strings
if(headerSplits.size() >= 2 && std::none_of(headerSplits.begin(), headerSplits.end(), [](std::string s){ return s.empty(); })) {
frame.getSource().assign(headerSplits[0]);
frame.getDestination().assign(headerSplits[1]);
for(unsigned int i = 2; i < headerSplits.size(); i++) {
frame.getPath().push_back(headerSplits[i]);
}
frame.getBody().assign(body);
ret = parseInt(frame);
if(!ret) {
frame.clear();
}
}
}
}
return ret;
}
bool CAPRSParser::parseInt(CAPRSFrame& frame)
{
APRS_FRAME_TYPE type = APFT_UNKNOWN;
unsigned char typeChar = frame.getBody()[0];
std::string body(frame.getBody().substr(1));//strip the type char for processing purposes
if(body.empty())
return false;
switch (typeChar)
{
case '!':
if(body[0] == '!') {
// This is ultimeter 200 weather station
return false;
}
[[fallthrough]];
case '=':
case '/':
case '@':
{
if(body.length() < 10) return false;//enough chars to have a chance to parse it ?
/* Normal or compressed location packet, with or without
* timestamp, with or without messaging capability
*
* ! and / have messaging, / and @ have a prepended timestamp
*/
type = APFT_POSITION;
if(typeChar == '/' || typeChar== '@')//With a prepended timestamp, jump over it.
body = body.substr(7U);
auto posChar = body[0];
if(valid_sym_table_compressed(posChar)//Compressed format
&& body.length() >= 13){//we need at least 13 char
//icom unsupported, ignore for now
return false;//parse_aprs_compressed(pb, body, body_end);
}
else if(posChar >= '0' && posChar <= '9' //Normal uncompressed format
&& body.length() >=19){//we need at least 19 chars for it to be valid
// if(ensureIsIcomCompatible(packet))
// return Parse(packet.Raw(), packet);
}
}
break;
case '$' :
if(body.length() > 10) {
type = APFT_NMEA;
}
break;
case ':':
// we have either message or telemetry labels or telemetry EQNS
if(body[9] == ':'
&& std::all_of(body.begin(), body.begin() + 9, [](char c){ return c == ' ' || c == '-' || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); })) {
type = APFT_MESSAGE;
//If reciepient is same as source and we donot have a sequence number at the end of message, Then it is telemetry
if(body.find(frame.getSource()) == 0U) {
auto eqnsPos = body.find("EQNS.");
auto parmPos = body.find("PARM.");
auto seqNumPos = body.find_last_of('{');
if((eqnsPos == 10U || parmPos == 10U) && seqNumPos == std::string::npos) {
type = APFT_TELEMETRY;
}
}
}
break;
case '>':
type = APFT_STATUS;
break;
case '#': /* Peet Bros U-II Weather Station */
case '*': /* Peet Bros U-I Weather Station */
case '_': /* Weather report without position */
type = APFT_WX;
break;
case '{':
type = APFT_UNKNOWN; //
break;
case 'T':
if(body[0] == '#') {
type = APFT_TELEMETRY;
}
break;
default:
type = APFT_UNKNOWN;
break;
}
frame.getType() = type;
return type != APFT_UNKNOWN;
}
bool CAPRSParser::valid_sym_table_compressed(unsigned char c)
{
return (c == '/' || c == '\\' || (c >= 0x41 && c <= 0x5A)
|| (c >= 0x61 && c <= 0x6A)); /* [\/\\A-Za-j] */
}
bool CAPRSParser::valid_sym_table_uncompressed(unsigned char c)
{
return (c == '/' || c == '\\' || (c >= 0x41 && c <= 0x5A)
|| (c >= 0x30 && c <= 0x39)); /* [\/\\A-Z0-9] */
}

@ -0,0 +1,35 @@
/*
* 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 <string>
#include <boost/algorithm/string.hpp>
#include "APRSFrame.h"
class CAPRSParser
{
public:
static bool parseFrame(const std::string& frameStr, CAPRSFrame& frame);
private:
static bool parseInt(CAPRSFrame& frame);
static bool valid_sym_table_compressed(unsigned char c);
static bool valid_sym_table_uncompressed(unsigned char c);
};

@ -0,0 +1,57 @@
/*
* 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 <boost/algorithm/string.hpp>
#include "APRSUtils.h"
void CAPRSUtils::dstarCallsignToAPRS(std::string& dstarCallsign)
{
if(dstarCallsign[dstarCallsign.length() - 1] == ' ') {
boost::trim(dstarCallsign);
} else {
//loop until got rid of all double blanks
while(dstarCallsign.find(" ") != std::string::npos) {
boost::replace_all(dstarCallsign, " ", " ");
}
boost::replace_all(dstarCallsign, " ", "-");//replace remaining blank with a -
}
}
unsigned int CAPRSUtils::calcGPSAIcomCRC(const std::string& gpsa)
{
unsigned int icomcrc = 0xFFFFU;
auto length = gpsa.length();
for (unsigned int j = 10U; j < length; j++) {
unsigned char ch = (unsigned char)gpsa[j];
for (unsigned int i = 0U; i < 8U; i++) {
bool xorflag = (((icomcrc ^ ch) & 0x01U) == 0x01U);
icomcrc >>= 1;
if (xorflag)
icomcrc ^= 0x8408U;
ch >>= 1;
}
}
return ~icomcrc & 0xFFFFU;
}

@ -0,0 +1,28 @@
/*
* 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 <string>
class CAPRSUtils
{
public:
static void dstarCallsignToAPRS(std::string& dstarCallsign);
static unsigned int calcGPSAIcomCRC(const std::string& gpsa);
};

@ -0,0 +1,12 @@
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
APRS.a: $(OBJS)
$(AR) rcs APRS.a $(OBJS)
%.o : %.cpp
$(CC) -I../BaseCommon $(CPPFLAGS) -MMD -MD -c $< -o $@
clean:
$(RM) *.o *.d APRS.a

@ -1,561 +0,0 @@
/*
* Copyright (C) 2010,2012,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2021 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 <cassert>
#include <cstring>
#include <string>
#include <boost/algorithm/string.hpp>
#include "APRSCollector.h"
#include "DStarDefines.h"
#include "Utils.h"
const unsigned int APRS_CSUM_LENGTH = 4U;
const unsigned int APRS_DATA_LENGTH = 300U;
const unsigned int SLOW_DATA_BLOCK_LENGTH = 6U;
const char APRS_OVERLAY = '\\';
const char APRS_SYMBOL = 'K';
CAPRSCollector::CAPRSCollector() :
m_state(AS_NONE),
m_ggaData(NULL),
m_ggaLength(0U),
m_ggaValid(false),
m_rmcData(NULL),
m_rmcLength(0U),
m_rmcValid(false),
m_crcData(NULL),
m_crcLength(0U),
m_crcValid(false),
m_buffer(NULL),
m_slowData(SS_FIRST),
m_collector(),
m_callsign()
{
m_ggaData = new unsigned char[APRS_DATA_LENGTH];
m_rmcData = new unsigned char[APRS_DATA_LENGTH];
m_crcData = new unsigned char[APRS_DATA_LENGTH];
m_buffer = new unsigned char[SLOW_DATA_BLOCK_LENGTH];
}
CAPRSCollector::~CAPRSCollector()
{
delete[] m_ggaData;
delete[] m_rmcData;
delete[] m_crcData;
delete[] m_buffer;
}
void CAPRSCollector::writeHeader(const std::string& callsign)
{
m_callsign = callsign;
}
bool CAPRSCollector::writeData(const unsigned char* data)
{
assert(data != NULL);
switch (m_slowData) {
case SS_FIRST:
m_buffer[0U] = data[0U] ^ SCRAMBLER_BYTE1;
m_buffer[1U] = data[1U] ^ SCRAMBLER_BYTE2;
m_buffer[2U] = data[2U] ^ SCRAMBLER_BYTE3;
m_slowData = SS_SECOND;
return false;
case SS_SECOND:
m_buffer[3U] = data[0U] ^ SCRAMBLER_BYTE1;
m_buffer[4U] = data[1U] ^ SCRAMBLER_BYTE2;
m_buffer[5U] = data[2U] ^ SCRAMBLER_BYTE3;
m_slowData = SS_FIRST;
break;
}
// Is it GPS data?
if ((m_buffer[0U] & SLOW_DATA_TYPE_MASK) == SLOW_DATA_TYPE_GPS)
return addGPSData(m_buffer + 1U);
return false;
}
void CAPRSCollector::reset()
{
m_state = AS_NONE;
m_ggaLength = 0U;
m_ggaValid = false;
m_rmcLength = 0U;
m_rmcValid = false;
m_crcLength = 0U;
m_crcValid = false;
m_slowData = SS_FIRST;
m_collector.clear();
m_callsign.clear();
}
void CAPRSCollector::sync()
{
m_slowData = SS_FIRST;
}
bool CAPRSCollector::addGPSData(const unsigned char* data)
{
assert(data != NULL);
m_collector += std::string((char*)data, 5U);
if (m_state == AS_GGA) {
addGGAData();
return false;
} else if (m_state == AS_RMC) {
return addRMCData();
} else if (m_state == AS_CRC) {
return addCRCData();
}
if (m_state != AS_GGA && m_collector.find("$GPGGA") != std::string::npos) {
m_state = AS_GGA;
m_ggaLength = 0U;
m_ggaValid = false;
m_rmcLength = 0U;
m_rmcValid = false;
return false;
} else if (m_state != AS_RMC && m_collector.find("$GPRMC") != std::string::npos) {
m_state = AS_RMC;
m_rmcLength = 0U;
m_rmcValid = false;
return false;
} else if (m_state != AS_CRC && m_collector.find("$$CRC") != std::string::npos) {
m_state = AS_CRC;
m_crcLength = 0U;
m_crcValid = false;
}
return false;
}
void CAPRSCollector::addGGAData()
{
std::string::size_type n2 = m_collector.find_last_of('\x0A');
if (n2 == std::string::npos)
return;
std::string::size_type n1 = m_collector.find("$GPGGA");
if (n1 == std::string::npos)
return;
if (n2 < n1)
return;
std::string::size_type len = n2 - n1;
if (len >= APRS_DATA_LENGTH) {
m_ggaLength = 0U;
m_ggaValid = false;
m_state = AS_NONE;
return;
}
m_ggaLength = 0U;
for (unsigned int i = n1; i <= n2; i++) {
m_ggaData[m_ggaLength] = m_collector[i];
m_ggaData[m_ggaLength] &= 0x7FU;
m_ggaLength++;
}
bool ret = checkXOR(m_ggaData + 1U, m_ggaLength - 1U);
if (ret) {
// CUtils::dump("$GPGGA Valid", m_ggaData, m_ggaLength);
m_ggaValid = true;
m_state = AS_RMC;
} else {
// CUtils::dump("$GPGGA Bad checksum", m_ggaData, m_ggaLength);
m_ggaLength = 0U;
m_ggaValid = false;
m_state = AS_RMC;
}
m_collector = m_collector.substr(n2);
}
bool CAPRSCollector::addRMCData()
{
std::string::size_type n2 = m_collector.find_last_of('\x0A');
if (n2 == std::string::npos)
return false;
std::string::size_type n1 = m_collector.find("$GPRMC");
if (n1 == std::string::npos)
return false;
if (n2 < n1)
return false;
unsigned int len = n2 - n1;
if (len >= APRS_DATA_LENGTH) {
m_rmcLength = 0U;
m_rmcValid = false;
m_state = AS_NONE;
return false;
}
m_rmcLength = 0U;
for (unsigned int i = n1; i <= n2; i++) {
m_rmcData[m_rmcLength] = m_collector[i];
m_rmcData[m_rmcLength] &= 0x7FU;
m_rmcLength++;
}
bool ret = checkXOR(m_rmcData + 1U, m_rmcLength - 1U);
if (ret) {
// CUtils::dump("$GPRMC Valid", m_rmcData, m_rmcLength);
m_rmcValid = true;
} else {
// CUtils::dump("$GPRMC Bad checksum", m_rmcData, m_rmcLength);
m_rmcLength = 0U;
m_rmcValid = false;
}
m_collector = m_collector.substr(n2);
m_state = AS_NONE;
return true;
}
bool CAPRSCollector::addCRCData()
{
std::string::size_type n2 = m_collector.find_last_of('\x0D');
if (n2 == std::string::npos)
return false;
std::string::size_type n1 = m_collector.find("$$CRC");
if (n1 == std::string::npos)
return false;
if (n2 < n1)
return false;
unsigned int len = n2 - n1;
if (len >= APRS_DATA_LENGTH) {
m_crcLength = 0U;
m_crcValid = false;
m_state = AS_NONE;
return false;
}
m_crcLength = 0U;
for (unsigned int i = n1; i <= n2; i++) {
m_crcData[m_crcLength] = m_collector[i];
m_crcLength++;
}
bool ret = checkCRC(m_crcData, m_crcLength);
if (ret) {
// CUtils::dump("$$CRC Valid", m_crcData, m_crcLength);
m_crcValid = true;
m_state = AS_NONE;
m_collector = m_collector.substr(n2);
return true;
} else {
// CUtils::dump("$$CRC Bad checksum", m_crcData, m_crcLength);
m_crcLength = 0U;
m_crcValid = false;
m_state = AS_NONE;
m_collector = m_collector.substr(n2);
return false;
}
}
unsigned int CAPRSCollector::getData(unsigned char* data, unsigned int length)
{
assert(data != NULL);
// Have we got GPS-A data?
if (m_crcValid) {
unsigned int len = m_crcLength - 10U;
if (len > length)
len = length;
::memcpy(data, m_crcData + 10U, len);
m_crcLength = 0U;
m_crcValid = false;
return len;
}
// Have we got GGA data?
if (m_ggaValid) {
unsigned int len = convertNMEA1(data, length);
m_ggaLength = 0U;
m_rmcLength = 0U;
m_ggaValid = false;
m_rmcValid = false;
return len;
}
// Have we got RMC data?
if (m_rmcValid) {
unsigned int len = convertNMEA2(data, length);
m_ggaLength = 0U;
m_rmcLength = 0U;
m_ggaValid = false;
m_rmcValid = false;
return len;
}
return 0U;
}
bool CAPRSCollector::checkXOR(const unsigned char* data, unsigned int length) const
{
unsigned int posStar = 0U;
for (unsigned int i = length - 1U; i > 0U; i--) {
if (data[i] == '*') {
posStar = i;
break;
}
}
if (posStar == 0U)
return false;
unsigned char csum = calcXOR(data, posStar);
char buffer[10U];
::sprintf(buffer, "%02X", csum);
return ::memcmp(buffer, data + posStar + 1U, 2U) == 0;
}
unsigned char CAPRSCollector::calcXOR(const unsigned char* buffer, unsigned int length) const
{
assert(buffer != NULL);
assert(length > 0U);
unsigned char res = 0U;
for (unsigned int i = 0U; i < length; i++)
res ^= buffer[i];
return res;
}
bool CAPRSCollector::checkCRC(const unsigned char* data, unsigned int length) const
{
unsigned int csum = calcCRC(data + 10U, length - 10U);
char buffer[10U];
::sprintf(buffer, "%04X", csum);
return ::memcmp(buffer, data + 5U, APRS_CSUM_LENGTH) == 0;
}
unsigned int CAPRSCollector::calcCRC(const unsigned char* buffer, unsigned int length) const
{
assert(buffer != NULL);
assert(length > 0U);
unsigned int icomcrc = 0xFFFFU;
for (unsigned int j = 0U; j < length; j++) {
unsigned char ch = buffer[j];
for (unsigned int i = 0U; i < 8U; i++) {
bool xorflag = (((icomcrc ^ ch) & 0x01U) == 0x01U);
icomcrc >>= 1;
if (xorflag)
icomcrc ^= 0x8408U;
ch >>= 1;
}
}
return ~icomcrc & 0xFFFFU;
}
unsigned int CAPRSCollector::convertNMEA1(unsigned char* data, unsigned int)
{
// Parse the $GPGGA string into tokens
char* pGGA[20U];
::memset(pGGA, 0x00U, 20U * sizeof(char*));
unsigned int nGGA = 0U;
char* str = (char*)m_ggaData;
while (nGGA < 20U) {
char* p = mystrsep(&str, ",\r\n");
pGGA[nGGA++] = p;
if (p == NULL)
break;
}
// Is there any position data?
if (pGGA[2U] == NULL || pGGA[3U] == NULL || pGGA[4U] == NULL || pGGA[5U] == NULL || ::strlen(pGGA[2U]) == 0U || ::strlen(pGGA[3U]) == 0U || ::strlen(pGGA[4U]) == 0 || ::strlen(pGGA[5U]) == 0)
return 0U;
// Is it a valid GPS fix?
if (::strcmp(pGGA[6U], "0") == 0)
return 0U;
char callsign[10U];
dstarCallsignToAPRS(m_callsign, callsign);
::sprintf((char*)data, "%s>APDPRS,DSTAR*:!%.7s%s%c%.8s%s%c", callsign, pGGA[2U], pGGA[3U], APRS_OVERLAY, pGGA[4U], pGGA[5U], APRS_SYMBOL);
// Get the bearing and speed from the RMC data
if (m_rmcValid) {
// Parse the $GPRMC string into tokens
char* pRMC[20U];
::memset(pRMC, 0x00U, 20U * sizeof(char*));
unsigned int nRMC = 0U;
str = (char*)m_rmcData;
for (;;) {
char* p = mystrsep(&str, ",\r\n");
pRMC[nRMC++] = p;
if (p == NULL)
break;
}
// Check that we have a bearing and speed
if (pRMC[7U] != NULL && pRMC[8U] != NULL && ::strlen(pRMC[7U]) > 0U && ::strlen(pRMC[8U]) > 0U) {
int bearing = ::atoi(pRMC[8U]);
int speed = ::atoi(pRMC[7U]);
::sprintf((char*)data + ::strlen((char*)data), "%03d/%03d", bearing, speed);
}
}
if (pGGA[9U] != NULL && ::strlen(pGGA[9U]) > 0U) {
// Convert altitude from metres to feet
int altitude = ::atoi(pGGA[9U]);
::sprintf((char*)data + ::strlen((char*)data), "/A=%06.0f", float(altitude) * 3.28F);
}
return ::strlen((char*)data);
}
unsigned int CAPRSCollector::convertNMEA2(unsigned char* data, unsigned int)
{
// Parse the $GPRMC string into tokens
char* pRMC[20U];
::memset(pRMC, 0x00U, 20U * sizeof(char*));
unsigned int nRMC = 0U;
char* str = (char*)m_rmcData;
while (nRMC < 20U) {
char* p = mystrsep(&str, ",\r\n");
pRMC[nRMC++] = p;
if (p == NULL)
break;
}
// Is there any position data?
if (pRMC[3U] == NULL || pRMC[4U] == NULL || pRMC[5U] == NULL || pRMC[6U] == NULL || ::strlen(pRMC[3U]) == 0U || ::strlen(pRMC[4U]) == 0U || ::strlen(pRMC[5U]) == 0 || ::strlen(pRMC[6U]) == 0)
return 0U;
// Is it a valid GPS fix?
if (::strcmp(pRMC[2U], "A") != 0)
return 0U;
char callsign[10U];
dstarCallsignToAPRS(m_callsign, callsign);
::sprintf((char*)data, "%s>APDPRS,DSTAR*:!%.7s%s%c%.8s%s%c", callsign, pRMC[3U], pRMC[4U], APRS_OVERLAY, pRMC[5U], pRMC[6U], APRS_SYMBOL);
if (pRMC[7U] != NULL && pRMC[8U] != NULL && ::strlen(pRMC[7U]) > 0U && ::strlen(pRMC[8U]) > 0U) {
int bearing = ::atoi(pRMC[8U]);
int speed = ::atoi(pRMC[7U]);
::sprintf((char*)data + ::strlen((char*)data), "%03d/%03d", bearing, speed);
}
return ::strlen((char*)data);
}
void CAPRSCollector::dstarCallsignToAPRS(const std::string& dstarCallsign, char* aprsCallsign) const
{
assert(aprsCallsign != NULL);
std::string dstarcallsignTmp(dstarCallsign);
if(dstarcallsignTmp[dstarcallsignTmp.length() - 1] == ' ') {
boost::trim(dstarcallsignTmp);
} else {
//loop until got rid of all double blanks
while(dstarcallsignTmp.find(" ") != std::string::npos) {
boost::replace_all(dstarcallsignTmp, " ", " ");
}
boost::replace_all(dstarcallsignTmp, " ", "-");//replace remaining blank with a -
}
unsigned int i = 0U;
for (; i < dstarcallsignTmp.length(); i++) {
aprsCallsign[i] = dstarcallsignTmp[i];
}
aprsCallsign[i] = '\0';
// std::string first = dstarCallsign.BeforeFirst(wxT(' '));
// std::string last = dstarCallsign.AfterLast(wxT(' '));
// if (last.empty() || first == last) {
// unsigned int n = 0U;
// for (unsigned int i = 0U; i < first.length(); i++)
// aprsCallsign[n++] = first[i];
// aprsCallsign[n++] = '\0';
// } else {
// unsigned int n = 0U;
// for (unsigned int i = 0U; i < first.length(); i++)
// aprsCallsign[n++] = first[i];
// aprsCallsign[n++] = '-';
// for (unsigned int i = 0U; i < last.length(); i++)
// aprsCallsign[n++] = last[i];
// aprsCallsign[n++] = '\0';
// }
}
// Source found at <http://unixpapa.com/incnote/string.html>
char* CAPRSCollector::mystrsep(char** sp, const char* sep) const
{
if (sp == NULL || *sp == NULL || **sp == '\0')
return NULL;
char* s = *sp;
char* p = s + ::strcspn(s, sep);
if (*p != '\0')
*p++ = '\0';
*sp = p;
return s;
}

@ -1,83 +0,0 @@
/*
* Copyright (C) 2010,2012,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2021 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.
*/
#ifndef APRSCollector_H
#define APRSCollector_H
#include "Defs.h"
enum APRS_STATE {
AS_NONE,
AS_GGA,
AS_RMC,
AS_CRC
};
class CAPRSCollector {
public:
CAPRSCollector();
~CAPRSCollector();
void writeHeader(const std::string& callsign);
bool writeData(const unsigned char* data);
void reset();
void sync();
unsigned int getData(unsigned char* data, unsigned int length);
private:
APRS_STATE m_state;
unsigned char* m_ggaData;
unsigned int m_ggaLength;
bool m_ggaValid;
unsigned char* m_rmcData;
unsigned int m_rmcLength;
bool m_rmcValid;
unsigned char* m_crcData;
unsigned int m_crcLength;
bool m_crcValid;
unsigned char* m_buffer;
SLOWDATA_STATE m_slowData;
std::string m_collector;
std::string m_callsign;
bool addGPSData(const unsigned char* data);
void addGGAData();
bool addRMCData();
bool addCRCData();
bool checkXOR(const unsigned char* data, unsigned int length) const;
unsigned char calcXOR(const unsigned char* buffer, unsigned int length) const;
bool checkCRC(const unsigned char* data, unsigned int length) const;
unsigned int calcCRC(const unsigned char* buffer, unsigned int length) const;
unsigned int convertNMEA1(unsigned char* data, unsigned int length);
unsigned int convertNMEA2(unsigned char* data, unsigned int length);
void dstarCallsignToAPRS(const std::string& dstarCallsign, char* aprsCallsign) const;
char* mystrsep(char** sp, const char* sep) const;
};
#endif

@ -43,7 +43,7 @@ bool CConfig::load()
std::ifstream file; std::ifstream file;
file.open(m_filename, std::ios::in); file.open(m_filename, std::ios::in);
if(!file.is_open()) { if(!file.is_open()) {
CLog::logError("Failed to open configuration file %s", m_filename); CLog::logError("Failed to open configuration file %s", m_filename.c_str());
return false; return false;
} }
@ -93,33 +93,22 @@ void CConfig::stripComment(std::string& s) const
free(sdup);// could we use delete sdup here? free(sdup);// could we use delete sdup here?
} }
TConfigValue * CConfig::readKeyAndValue(const std::string s) const TConfigValue * CConfig::readKeyAndValue(const std::string& s) const
{ {
TConfigValue* res = nullptr; TConfigValue* res = nullptr;
auto sCopy = boost::trim_copy(s);
char * sdup = strdup(boost::trim_copy(s).c_str()); auto equalPos = sCopy.find_first_of('=');
if(equalPos != std::string::npos) {
char * keyPtr = strtok(sdup, "=");
std::string key(keyPtr != nullptr ? keyPtr : "");
boost::trim(key);
if(!key.empty()) {
char * valuePtr = strtok(nullptr, "=");
std::string value(valuePtr != nullptr? valuePtr : "");
res = new TConfigValue; res = new TConfigValue;
res->m_key = key; res->m_key.assign(sCopy.substr(0, equalPos));
res->m_value = boost::trim_copy(value); res->m_value.assign(sCopy.substr(equalPos + 1));
} }
free(sdup);// could we use delete sdup here?
return res; return res;
} }
bool CConfig::getValue(const std::string &section, const std::string& key, bool &value, bool defaultValue) const bool CConfig::getValue(const std::string &section, const std::string& key, bool &value, bool defaultValue) const
{ {
value = defaultValue;
std::string valueTemp; std::string valueTemp;
std::string dafaultValueStr = defaultValue ? "true" : "false"; std::string dafaultValueStr = defaultValue ? "true" : "false";
bool ret = getValue(section, key, valueTemp, dafaultValueStr, {"true", "1", "false", "0"}); bool ret = getValue(section, key, valueTemp, dafaultValueStr, {"true", "1", "false", "0"});

@ -106,7 +106,7 @@ public:
private: private:
void stripComment(std::string& s) const; void stripComment(std::string& s) const;
TConfigValue * readKeyAndValue(const std::string s) const; TConfigValue * readKeyAndValue(const std::string& s) const;
TConfigValue * lookupValue(const std::string& section, const std::string& key) const; TConfigValue * lookupValue(const std::string& section, const std::string& key) const;
std::string m_filename; std::string m_filename;

@ -19,11 +19,11 @@
#pragma once #pragma once
#include <ctime>
#include <string> #include <string>
#include <vector> #include <vector>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <mutex> #include <mutex>
#include <sstream>
#include <cassert> #include <cassert>
#include "StringUtils.h" #include "StringUtils.h"
@ -36,45 +36,46 @@ private:
static bool m_addedTargets; static bool m_addedTargets;
static std::recursive_mutex m_targetsMutex; static std::recursive_mutex m_targetsMutex;
static void getTimeStamp(std::string & s); static void getTimeStamp(std::string& s);
template<typename... Args> static void formatLogMessage(std::string& output, LOG_SEVERITY severity, const std::string & f, Args... args) template<typename... Args>
static void formatLogMessage(std::string& output, LOG_SEVERITY severity, const std::string & f, Args... args)
{ {
assert(severity != LOG_NONE); assert(severity != LOG_NONE);
std::string severityStr; std::string severityStr(" ");
switch (severity) switch (severity)
{ {
case LOG_DEBUG: case LOG_DEBUG:
severityStr = "DEBUG "; severityStr.assign("DEBUG ");
break; break;
case LOG_ERROR: case LOG_ERROR:
severityStr = "ERROR "; severityStr.assign("ERROR ");
break; break;
case LOG_FATAL: case LOG_FATAL:
severityStr = "FATAL "; severityStr.assign("FATAL ");
break; break;
case LOG_INFO : case LOG_INFO :
severityStr = "INFO "; severityStr.assign("INFO ");
break; break;
case LOG_WARNING: case LOG_WARNING:
severityStr = "WARNING"; severityStr.assign("WARNING");
break; break;
case LOG_TRACE: case LOG_TRACE:
severityStr = "TRACE "; severityStr.assign("TRACE ");
break; break;
default: default:
break; break;
} }
std::string message = CStringUtils::string_format(f, args...); std::string timestamp;
boost::trim(message); getTimeStamp(timestamp);
std::string timeUtc;
getTimeStamp(timeUtc);
std::stringstream s;
s << "[" << timeUtc << "] [" << severityStr << "] " << message << std::endl;
output = s.str(); std::string f2("[%s] [%s] ");
f2.append(f);
CStringUtils::string_format_in_place(output, f2, timestamp.c_str(), severityStr.c_str(), args...);
boost::trim_if(output, [](char c){ return c == '\n' || c == '\r' || c == ' ' || c == '\t'; });
output.push_back('\n');
} }
public: public:
@ -82,6 +83,11 @@ public:
static void addTarget(CLogTarget * target); static void addTarget(CLogTarget * target);
static void finalise(); static void finalise();
template<typename... Args> static void logTrace(const std::string & f, Args... args)
{
log(LOG_TRACE, f, args...);
}
template<typename... Args> static void logDebug(const std::string & f, Args... args) template<typename... Args> static void logDebug(const std::string & f, Args... args)
{ {
log(LOG_DEBUG, f, args...); log(LOG_DEBUG, f, args...);

@ -0,0 +1,103 @@
/*
* 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 <iostream>
#include <fstream>
#include <chrono>
#include <ctime>
#include "LogFileTarget.h"
#define LOG_FILE_ROOT "dstargateway"
CLogFileTarget::CLogFileTarget(LOG_SEVERITY logLevel, const std::string & dir, bool rotate) :
CLogTarget(logLevel),
m_dir(dir),
m_rotate(rotate),
m_file(),
m_day(0)
{
}
CLogFileTarget::~CLogFileTarget()
{
if(m_file.is_open()) {
m_file.close();
}
}
void CLogFileTarget::printLogIntFixed(const std::string& msg)
{
if(m_file.is_open()) {
m_file << msg;
m_file.flush();
return;
}
std::string filename(m_dir);
if(filename[filename.length() - 1U] != '/') filename.push_back('/');
filename.append(LOG_FILE_ROOT).append(".log");
m_file.open(filename, std::ios::app);
if(m_file.is_open()) {
printLogIntFixed(msg);
}
else {
std::cerr << "FAILED TO OPEN LOG FILE :" << filename;
}
}
void CLogFileTarget::printLogIntRotate(const std::string& msg)
{
std::time_t now = std::time(0);
std::tm* now_tm = std::gmtime(&now);
if(now_tm->tm_yday != m_day) {
m_day = now_tm->tm_yday;
if(m_file.is_open()) {
m_file.close();
}
}
if(!m_file.is_open()) {
std::string filename(m_dir);
if(filename[filename.length() - 1U] != '/') filename.push_back('/');
char buf[64];
std::strftime(buf, 42, "-%Y-%m-%d", now_tm);
filename.append(LOG_FILE_ROOT).append(buf).append(".log");
m_file.open(filename, std::ios::app);
if(!m_file.is_open()) {
std::cerr << "FAILED TO OPEN LOG FILE :" << filename;
}
}
if(m_file.is_open()) {
m_file << msg;
m_file.flush();
return;
}
}
void CLogFileTarget::printLogInt(const std::string& msg)
{
m_file.seekp(0, std::ios::end);
if(m_rotate)
printLogIntRotate(msg);
else
printLogIntFixed(msg);
}

@ -19,6 +19,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <fstream>
#include "LogTarget.h" #include "LogTarget.h"
@ -26,11 +27,17 @@ class CLogFileTarget : public CLogTarget
{ {
public: public:
CLogFileTarget(LOG_SEVERITY logLevel, const std::string& directory, bool rotate); CLogFileTarget(LOG_SEVERITY logLevel, const std::string& directory, bool rotate);
~CLogFileTarget();
protected: protected:
virtual void printLogInt(const std::string& msg); virtual void printLogInt(const std::string& msg);
private: private:
void printLogIntRotate(const std::string& msg);
void printLogIntFixed(const std::string& msg);
std::string buildFileName();
std::string m_dir; std::string m_dir;
bool m_rotate; bool m_rotate;
std::fstream m_file;
int m_day;
}; };

@ -0,0 +1,12 @@
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
BaseCommon.a: $(OBJS)
$(AR) rcs BaseCommon.a $(OBJS)
%.o : %.cpp
$(CC) $(CPPFLAGS) -MMD -MD -c $< -o $@
clean:
$(RM) *.o *.d BaseCommon.a

@ -0,0 +1,121 @@
/*
* Copyright (c) 2022 by Geoffrey Merck F4FXL / KC3FRA
* Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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 <sys/types.h>
#include <cstring>
#include "NetUtils.h"
bool CNetUtils::lookupV4(const std::string& hostname, sockaddr_storage& addr)
{
struct addrinfo hints;
::memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
return lookup(hostname, addr, hints);
}
bool CNetUtils::lookupV6(const std::string& hostname, sockaddr_storage& addr)
{
struct addrinfo hints;
::memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
return lookup(hostname, addr, hints);
}
bool CNetUtils::lookup(const std::string& hostname, sockaddr_storage& addr)
{
struct addrinfo hints;
::memset(&hints, 0, sizeof(hints));
return lookup(hostname, addr, hints);
}
bool CNetUtils::lookup(const std::string& hostname, sockaddr_storage& addr, struct addrinfo& hints)
{
struct addrinfo *res;
int err = getaddrinfo(hostname.c_str(), nullptr, &hints, &res);
if(err != 0) {
::memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
lookup("255.255.255.255", addr, hints);
return false;
}
::memcpy(&addr, res->ai_addr, res->ai_addrlen);
::freeaddrinfo(res);
return true;
}
bool CNetUtils::match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type)
{
if (addr1.ss_family != addr2.ss_family)
return false;
if (type == IMT_ADDRESS_AND_PORT) {
switch (addr1.ss_family) {
case AF_INET:
struct sockaddr_in *in_1, *in_2;
in_1 = (struct sockaddr_in*)&addr1;
in_2 = (struct sockaddr_in*)&addr2;
return (in_1->sin_addr.s_addr == in_2->sin_addr.s_addr) && (in_1->sin_port == in_2->sin_port);
case AF_INET6:
struct sockaddr_in6 *in6_1, *in6_2;
in6_1 = (struct sockaddr_in6*)&addr1;
in6_2 = (struct sockaddr_in6*)&addr2;
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr) && (in6_1->sin6_port == in6_2->sin6_port);
default:
return false;
}
} else if (type == IMT_ADDRESS_ONLY) {
switch (addr1.ss_family) {
case AF_INET:
struct sockaddr_in *in_1, *in_2;
in_1 = (struct sockaddr_in*)&addr1;
in_2 = (struct sockaddr_in*)&addr2;
return in_1->sin_addr.s_addr == in_2->sin_addr.s_addr;
case AF_INET6:
struct sockaddr_in6 *in6_1, *in6_2;
in6_1 = (struct sockaddr_in6*)&addr1;
in6_2 = (struct sockaddr_in6*)&addr2;
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr);
default:
return false;
}
} else {
return false;
}
}
void CNetUtils::setPort(struct sockaddr_storage& addr, in_port_t port)
{
switch (addr.ss_family)
{
case AF_INET:
TOIPV4(addr)->sin_port = port;
break;
case AF_INET6:
TOIPV6(addr)->sin6_port = port;
default:
break;
}
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2022 by Geoffrey Merck F4FXL / KC3FRA
* Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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 <string>
#include <netdb.h>
#include <sys/socket.h>
#define TOIPV6(s) ((struct sockaddr_in6*)&s)
#define TOIPV4(s) (((struct sockaddr_in*)&s))
#define GETPORT(s) (s.ss_family == AF_INET6 ? TOIPV6(s)->sin6_port : TOIPV4(s)->sin_port)
#define SETPORT(s, p) (if(s.ss_family == AF_INET6)TOIPV6(s)->sin6_port = p;else TOIPV4(s)->sin_port = p;)
enum IPMATCHTYPE {
IMT_ADDRESS_AND_PORT,
IMT_ADDRESS_ONLY
};
class CNetUtils
{
public:
static bool lookupV6(const std::string& hostname, sockaddr_storage& addr);
static bool lookupV4(const std::string& hostname, sockaddr_storage& addr);
static bool lookup(const std::string& hostname, sockaddr_storage& addr);
static bool lookup(const std::string& hostname, sockaddr_storage& addr, struct addrinfo& hints);
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type);
static void setPort(struct sockaddr_storage& addr, in_port_t port);
};

@ -0,0 +1,60 @@
/*
* Copyright (c) 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 <cassert>
#include <boost/algorithm/string.hpp>
#include "ProgramArgs.h"
void CProgramArgs::eatArguments(int argc, const char *argv[], std::unordered_map<std::string, std::string>& namedArgs, std::vector<std::string>& positionalArgs)
{
assert(argv != nullptr);
namedArgs.clear();
positionalArgs.clear();
std::vector<std::string> programArgs;
// Copy to a vector for easier handling, also skip program name
for(int i = 1;i < argc; i++) {
if(argv[i] != nullptr) {
programArgs.push_back(std::string(argv[i]));
}
}
// Consume Named args first
for(auto it = programArgs.begin(); it != programArgs.end();) {
if(boost::starts_with(*it, "-")) {
std::string argName = boost::trim_left_copy_if(*it, [] (char c) { return c == '-'; });
if(!argName.empty()) {
namedArgs[argName] = "";
it = programArgs.erase(it);
if(it != programArgs.end()) {
namedArgs[argName] = *it;
it = programArgs.erase(it);
}
}
}
else {
it++;
}
}
//ProgramArgs now only contains pôsitional Args
positionalArgs.assign(programArgs.begin(), programArgs.end());
}

@ -0,0 +1,29 @@
/*
* Copyright (c) 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 <string>
#include <vector>
#include <unordered_map>
class CProgramArgs
{
public:
static void eatArguments(int argc, const char *argv[], std::unordered_map<std::string, std::string>& namedArgs, std::vector<std::string>& positionalArgs);
};

@ -36,8 +36,6 @@ public:
assert(length > 0U); assert(length > 0U);
m_buffer = new T[length]; m_buffer = new T[length];
::memset(m_buffer, 0x00, length * sizeof(T));
} }
~CRingBuffer() ~CRingBuffer()

@ -0,0 +1,52 @@
/*
* 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 <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "StringUtils.h"
size_t CStringUtils::find_nth(const std::string& haystack, char needle, size_t nth)
{
size_t matches = 0U;
auto haystackLength = haystack.length();
for(size_t i = 0; i < haystackLength; i++) {
if(haystack[i] == needle) {
matches++;
if(matches == nth)
return i;
}
}
return std::string::npos;
}
unsigned int CStringUtils::stringToPort(const std::string& s)
{
unsigned int port = 0U;
std::string ls = boost::trim_copy(s);
if(!ls.empty() && std::all_of(ls.begin(), ls.end(), [](char c){ return c >= '0' && c <= '9'; })) {
auto portTemp = boost::lexical_cast<unsigned int>(ls);
if(portTemp > 0U && portTemp <= 65535U)
port = portTemp;
}
return port;
}

@ -1,7 +1,5 @@
/* /*
* Copyright (C) 2009-2011,2013 by Jonathan Naylor G4KLX * Copyright (c) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA
* Copyright (c) 2017 by Thomas A. Early N7TAE
* Copyright (c) 2021 by Geoffrey Merck F4FXL / KC3FRA
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -23,19 +21,37 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include <algorithm>
#define string_right(s,l) (s.substr(s.length() - l, l)) #define string_right(s,l) (s.substr(s.length() - l, l))
#define string_is_blank(s) (std::all_of(s.begin(), s.end(), [](char c) { return c == ' '; }))
#define string_is_blank_or_empty(s) (s.empty() || std::all_of(s.begin(), s.end(), [](char c) { return c == ' '; }))
class CStringUtils { class CStringUtils {
public: public:
template<typename ... Args> template<typename ... Args>
static std::string string_format( const std::string& format, Args ... args ) static std::string string_format( const std::string& format, Args ... args )
{
std::string ret;
string_format_in_place(ret, format, args...);
return ret;
}
template<typename ... Args>
static void string_format_in_place(std::string& output, const std::string& format, Args ... args )
{ {
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); } if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
auto size = static_cast<size_t>( size_s ); auto size = static_cast<size_t>( size_s );
auto buf = std::make_unique<char[]>( size ); auto buf = std::make_unique<char[]>( size );
std::snprintf( buf.get(), size, format.c_str(), args ... ); std::snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
output.reserve(size);
output.assign(buf.get(), size - 1); // -1 because we do not need trailing '\0'
} }
static size_t find_nth(const std::string& haystack, char needle, size_t nth);
static unsigned int stringToPort(const std::string& s);
}; };

@ -201,19 +201,15 @@ bool CTCPReaderWriterClient::write(const unsigned char* buffer, unsigned int len
bool CTCPReaderWriterClient::writeLine(const std::string& line) bool CTCPReaderWriterClient::writeLine(const std::string& line)
{ {
std::string lineCopy(line); if(line.empty())
if(lineCopy.size() > 0 && lineCopy.at(lineCopy.size() - 1) != '\n') return true;
lineCopy.push_back('\n');
bool ret = write((unsigned char *)line.c_str(), line.length());
//stupidly write one char after the other
size_t len = lineCopy.size(); if(line[line.length() - 1] != '\n')//make sure we send a newline
bool result = true; ret = writeLine("\n") && ret;
for(size_t i = 0; i < len && result; i++){
unsigned char c = lineCopy.at(i);
result = write(&c , 1);
}
return result; return ret;
} }
void CTCPReaderWriterClient::close() void CTCPReaderWriterClient::close()

@ -21,6 +21,7 @@
#include <string.h> #include <string.h>
#include "UDPReaderWriter.h" #include "UDPReaderWriter.h"
#include "Log.h" #include "Log.h"
#include "NetUtils.h"
CUDPReaderWriter::CUDPReaderWriter(const std::string& address, unsigned int port) : CUDPReaderWriter::CUDPReaderWriter(const std::string& address, unsigned int port) :
m_address(address), m_address(address),
@ -101,7 +102,7 @@ bool CUDPReaderWriter::open()
return true; return true;
} }
int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, struct sockaddr_storage& addr)
{ {
// Check that the readfrom() won't block // Check that the readfrom() won't block
fd_set readFds; fd_set readFds;
@ -122,8 +123,7 @@ int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr&
if (ret == 0) if (ret == 0)
return 0; return 0;
sockaddr_in addr; socklen_t size = sizeof(addr);
socklen_t size = sizeof(sockaddr_in);
ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size);
if (len <= 0) { if (len <= 0) {
@ -131,22 +131,54 @@ int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr&
return -1; return -1;
} }
address = addr.sin_addr;
port = ntohs(addr.sin_port);
return len; return len;
} }
int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port)
{
struct sockaddr_storage addr;
auto res = read(buffer, length, addr);
if(res >= 0 && addr.ss_family == AF_INET) {
address = TOIPV4(addr)->sin_addr;
port = ntohs(TOIPV4(addr)->sin_port);
}
return res;
}
bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port) bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port)
{ {
sockaddr_in addr; struct sockaddr_storage addr;
::memset(&addr, 0x00, sizeof(sockaddr_in)); ::memset(&addr, 0, sizeof(sockaddr_storage));
addr.ss_family = AF_INET;
TOIPV4(addr)->sin_addr = address;
TOIPV4(addr)->sin_port = htons(port);
addr.sin_family = AF_INET; return write(buffer, length, addr);
addr.sin_addr = address; // sockaddr_in addr;
addr.sin_port = htons(port); // ::memset(&addr, 0x00, sizeof(sockaddr_in));
ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, 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)
{
ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(addr));
if (ret < 0) { if (ret < 0) {
CLog::logError("Error returned from sendto (port: %u), err: %s\n", m_port, strerror(errno)); CLog::logError("Error returned from sendto (port: %u), err: %s\n", m_port, strerror(errno));
return false; return false;
@ -158,6 +190,8 @@ bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, c
return true; return true;
} }
void CUDPReaderWriter::close() void CUDPReaderWriter::close()
{ {
::close(m_fd); ::close(m_fd);

@ -41,8 +41,10 @@ public:
bool open(); bool open();
int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port); int read(unsigned char* buffer, unsigned int length, struct sockaddr_storage& addr);
int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port);
bool write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port); bool write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port);
bool write(const unsigned char* buffer, unsigned int length, const struct sockaddr_storage& addr);
void close(); void close();

@ -66,4 +66,25 @@ public:
return dest.u; return dest.u;
} }
// https://stackoverflow.com/questions/1001307/detecting-endianness-programmatically-in-a-c-program
static bool is_big_endian()
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
// Ersatz for macro wxINT32_SWAP_ON_BE
template <typename T>
static T swap_endian_be(T u)
{
if(is_big_endian())
return swap_endian(u);
return u;
}
}; };

@ -0,0 +1,114 @@
/*
* Copyright (C) 2010,2012,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2021 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 <cassert>
#include <cstring>
#include <string>
#include <boost/algorithm/string.hpp>
#include "APRSCollector.h"
#include "DStarDefines.h"
#include "Utils.h"
#include "NMEASentenceCollector.h"
#include "GPSACollector.h"
#include "RSMS1AMessageCollector.h"
#include "SlowDataCollectorThrottle.h"
CAPRSCollector::CAPRSCollector() :
m_collectors()
{
m_collectors.push_back(new CRSMS1AMessageCollector()); // we do not throttle messages, they have highest priority !
m_collectors.push_back(new CSlowDataCollectorThrottle(new CGPSACollector(), 10U));
m_collectors.push_back(new CSlowDataCollectorThrottle(new CNMEASentenceCollector("$GPGGA"), 10U));
m_collectors.push_back(new CSlowDataCollectorThrottle(new CNMEASentenceCollector("$GPGLL"), 10U));
m_collectors.push_back(new CSlowDataCollectorThrottle(new CNMEASentenceCollector("$GPVTG"), 10U));
m_collectors.push_back(new CSlowDataCollectorThrottle(new CNMEASentenceCollector("$GPRMC"), 10U));
m_collectors.push_back(new CSlowDataCollectorThrottle(new CNMEASentenceCollector("$GPGSA"), 10U));
m_collectors.push_back(new CSlowDataCollectorThrottle(new CNMEASentenceCollector("$GPGSV"), 10U));
}
CAPRSCollector::~CAPRSCollector()
{
for(auto collector : m_collectors) {
delete collector;
}
m_collectors.clear();
}
void CAPRSCollector::writeHeader(const std::string& callsign)
{
for(auto collector : m_collectors) {
collector->setMyCall(callsign);
}
}
bool CAPRSCollector::writeData(const unsigned char* data)
{
bool ret = false;
for(auto collector : m_collectors) {
bool ret2 = collector->writeData(data);
ret = ret || ret2;
}
return ret;
}
void CAPRSCollector::reset()
{
for(auto collector : m_collectors) {
collector->reset();
}
}
void CAPRSCollector::sync()
{
for(auto collector : m_collectors) {
collector->sync();
}
}
unsigned int CAPRSCollector::getData(unsigned char dataType, unsigned char* data, unsigned int length)
{
for(auto collector : m_collectors) {
if(collector->getDataType() == dataType) {
unsigned int res = collector->getData(data, length);
if(res > 0U)
return res;
}
}
return 0U;
}
void CAPRSCollector::getData(std::function<void(const std::string&)> dataHandler)
{
for(auto collector : m_collectors) {
std::string data;
if(collector->getData(data)) {
dataHandler(data);
collector->reset();
}
}
}
void CAPRSCollector::clock(unsigned int ms)
{
for(auto collector : m_collectors) {
collector->clock(ms);
}
}

@ -0,0 +1,60 @@
/*
* Copyright (C) 2010,2012,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2021 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.
*/
#ifndef APRSCollector_H
#define APRSCollector_H
#include <vector>
#include <functional>
#include "SlowDataCollector.h"
#include "Defs.h"
enum APRS_STATE {
AS_NONE,
AS_GGA,
AS_RMC,
AS_MSG,
AS_CRC
};
class CAPRSCollector {
public:
CAPRSCollector();
~CAPRSCollector();
void writeHeader(const std::string& callsign);
bool writeData(const unsigned char* data);
void reset();
void sync();
unsigned int getData(unsigned char dataType, unsigned char* data, unsigned int length);
void getData(std::function<void(const std::string&)> dataHandler);
void clock(unsigned int ms);
private:
std::vector<ISlowDataCollector *> m_collectors;
};
#endif

@ -106,6 +106,7 @@ void CAPRSEntry::clock(unsigned int ms)
{ {
m_linkStatus.clock(ms); m_linkStatus.clock(ms);
m_timer.clock(ms); m_timer.clock(ms);
m_collector->clock(ms);
} }
bool CAPRSEntry::isOK() bool CAPRSEntry::isOK()

@ -29,7 +29,7 @@ CAPRSIdFrameProvider(20U) // Initial timeout of 20 seconds
} }
bool CAPRSFixedIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector<std::string>& frames) bool CAPRSFixedIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector<CAPRSFrame *>& frames)
{ {
if (entry == nullptr) if (entry == nullptr)
return false; return false;
@ -107,24 +107,33 @@ bool CAPRSFixedIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, c
boost::replace_all(lat, ",", "."); boost::replace_all(lat, ",", ".");
boost::replace_all(lon, ",", "."); boost::replace_all(lon, ",", ".");
std::string output = CStringUtils::string_format("%s-S>APD5T1,TCPIP*,qAC,%s-GS:;%-7s%-2s*%02d%02d%02dz%s%cD%s%caRNG%04.0lf/A=%06.0lf %s %s\r\n", std::string body = CStringUtils::string_format(";%-7s%-2s*%02d%02d%02dz%s%cD%s%caRNG%04.0lf/A=%06.0lf %s %s\r\n",
gateway.c_str(), gateway.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(),
tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_mday, tm->tm_hour, tm->tm_min,
lat.c_str(), (entry->getLatitude() < 0.0F) ? 'S' : 'N', lat.c_str(), (entry->getLatitude() < 0.0F) ? 'S' : 'N',
lon.c_str(), (entry->getLongitude() < 0.0F) ? 'W' : 'E', lon.c_str(), (entry->getLongitude() < 0.0F) ? 'W' : 'E',
entry->getRange() * 0.6214, entry->getAGL() * 3.28, band.c_str(), desc.c_str()); entry->getRange() * 0.6214, entry->getAGL() * 3.28, band.c_str(), desc.c_str());
CAPRSFrame * frame = new CAPRSFrame(gateway + "-S",
"APD5T1",
{ "TCPIP*", "qAC" , gateway + "-GS" },
body, APFT_OBJECT);
frames.push_back(output); frames.push_back(frame);
if (entry->getBand().length() == 1U) { if (entry->getBand().length() == 1U) {
output = CStringUtils::string_format("%s-%s>APD5T2,TCPIP*,qAC,%s-%sS:!%s%cD%s%c&RNG%04.0lf/A=%06.0lf %s %s\r\n", body = CStringUtils::string_format("!%s%cD%s%c&RNG%04.0lf/A=%06.0lf %s %s\r\n",
entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(),
lat.c_str(), (entry->getLatitude() < 0.0F) ? 'S' : 'N', lat.c_str(), (entry->getLatitude() < 0.0F) ? 'S' : 'N',
lon.c_str(), (entry->getLongitude() < 0.0F) ? 'W' : 'E', lon.c_str(), (entry->getLongitude() < 0.0F) ? 'W' : 'E',
entry->getRange() * 0.6214, entry->getAGL() * 3.28, band.c_str(), desc.c_str()); entry->getRange() * 0.6214, entry->getAGL() * 3.28, band.c_str(), desc.c_str());
frames.push_back(output); frame = new CAPRSFrame(entry->getCallsign() + "-" + entry->getBand(),
"APD5T2",
{ "TCPIP*", "qAC", entry->getCallsign() + "-" + entry->getBand() + "S"},
body, APFT_POSITION);
frames.push_back(frame);
} }
setTimeout(20U * 60U);//20 minutes, plenty enough for fixed setTimeout(20U * 60U);//20 minutes, plenty enough for fixed

@ -26,5 +26,5 @@ public:
CAPRSFixedIdFrameProvider(); CAPRSFixedIdFrameProvider();
protected: protected:
virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector<std::string>& frames); virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector<CAPRSFrame *>& frames);
}; };

@ -16,7 +16,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#ifdef USE_DGPS #ifdef USE_GPSD
#include <cmath> #include <cmath>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -56,7 +56,7 @@ void CAPRSGPSDIdFrameProvider::close()
} }
} }
bool CAPRSGPSDIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector<std::string>& frames) bool CAPRSGPSDIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector<CAPRSFrame *>& frames)
{ {
if(!m_hasConnection) { if(!m_hasConnection) {
this->start(); this->start();
@ -169,40 +169,44 @@ bool CAPRSGPSDIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, co
boost::replace_all(lat, ",", "."); boost::replace_all(lat, ",", ".");
boost::replace_all(lon, ",", "."); boost::replace_all(lon, ",", ".");
std::string output1 = CStringUtils::string_format("%s-S>APD5T1,TCPIP*,qAC,%s-GS:;%-7s%-2s*%02d%02d%02dz%s%cD%s%ca/A=%06.0lf", std::string body = CStringUtils::string_format(";%-7s%-2s*%02d%02d%02dz%s%cD%s%ca/A=%06.0lf",
gateway.c_str(), gateway.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(),
tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_mday, tm->tm_hour, tm->tm_min,
lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N', lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N',
lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E', lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E',
rawAltitude * 3.28); rawAltitude * 3.28);
std::string output2;
if (bearingSet && velocitySet) if (bearingSet && velocitySet)
output2 = CStringUtils::string_format("%03.0lf/%03.0lf", rawBearing, rawVelocity * 0.539957F); body.append(CStringUtils::string_format("%03.0lf/%03.0lf", rawBearing, rawVelocity * 0.539957F));
std::string output3; body.append(CStringUtils::string_format("RNG%04.0lf %s %s\r\n", entry->getRange() * 0.6214, band.c_str(), desc.c_str()));
output3 = CStringUtils::string_format("RNG%04.0lf %s %s\r\n", entry->getRange() * 0.6214, band.c_str(), desc.c_str());
CLog::logDebug("APRS ==> %s%s%s", output1.c_str(), output2.c_str(), output3.c_str());
frames.push_back(output1.append(output2).append(output3)); CAPRSFrame * frame = new CAPRSFrame(gateway + "-S",
"APD5T1",
{ "TCPIP*", "qAC" , gateway + "-GS" },
body, APFT_OBJECT);
frames.push_back(frame);
if (entry->getBand().length() == 1U) { if (entry->getBand().length() == 1U) {
if (altitudeSet) if (altitudeSet)
output1 = CStringUtils::string_format("%s-%s>APD5T2,TCPIP*,qAC,%s-%sS:!%s%cD%s%c&/A=%06.0lf", body = CStringUtils::string_format("%s%cD%s%c&/A=%06.0lf",
entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(),
lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N', lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N',
lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E', lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E',
rawAltitude * 3.28); rawAltitude * 3.28);
else else
output1 = CStringUtils::string_format("%s-%s>APD5T2,TCPIP*,qAC,%s-%sS:!%s%cD%s%c&", body = CStringUtils::string_format("!%s%cD%s%c&",
entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(),
lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N', lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N',
lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E'); lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E');
CLog::logDebug("APRS ==> %s%s%s", output1.c_str(), output2.c_str(), output3.c_str()); frame = new CAPRSFrame(gateway,
"APD5T2",
{ "TCPIP*", "qAC" , gateway + "-GS" },
body, APFT_POSITION);
frames.push_back(output1.append(output2).append(output3)); frames.push_back(frame);
} }
setTimeout(60U * 5U);//5 Minutes is plenty enough we aint an APRS tracker ! setTimeout(60U * 5U);//5 Minutes is plenty enough we aint an APRS tracker !

@ -35,7 +35,7 @@ public:
virtual void close(); virtual void close();
protected: protected:
virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector<std::string>& frames); virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector<CAPRSFrame *>& frames);
private: private:
std::string m_gpsdAddress; std::string m_gpsdAddress;

@ -21,15 +21,20 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <cmath> #include <cmath>
#include <cassert> #include <cassert>
#include <algorithm>
#include "StringUtils.h" #include "StringUtils.h"
#include "Log.h" #include "Log.h"
#include "APRSWriter.h" #include "APRSHandler.h"
#include "DStarDefines.h" #include "DStarDefines.h"
#include "Defs.h" #include "Defs.h"
#include "Log.h" #include "Log.h"
#include "APRSFrame.h"
#include "APRSParser.h"
#include "APRSFormater.h"
#include "APRSUtils.h"
CAPRSWriter::CAPRSWriter(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address) : CAPRSHandler::CAPRSHandler(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address) :
m_thread(NULL), m_thread(NULL),
m_gateway(), m_gateway(),
m_address(), m_address(),
@ -42,14 +47,14 @@ m_idFrameProvider(nullptr)
assert(!gateway.empty()); assert(!gateway.empty());
assert(!password.empty()); assert(!password.empty());
m_thread = new CAPRSWriterThread(gateway, password, address, hostname, port); m_thread = new CAPRSHandlerThread(gateway, password, address, hostname, port);
m_gateway = gateway; m_gateway = gateway;
m_gateway = m_gateway.substr(0, LONG_CALLSIGN_LENGTH - 1U); m_gateway = m_gateway.substr(0, LONG_CALLSIGN_LENGTH - 1U);
boost::trim(m_gateway); boost::trim(m_gateway);
} }
CAPRSWriter::~CAPRSWriter() CAPRSHandler::~CAPRSHandler()
{ {
for(auto it = m_array.begin(); it != m_array.end(); it++) { for(auto it = m_array.begin(); it != m_array.end(); it++) {
delete it->second; delete it->second;
@ -58,7 +63,7 @@ CAPRSWriter::~CAPRSWriter()
m_array.clear(); m_array.clear();
} }
void CAPRSWriter::setPort(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl) void CAPRSHandler::setPort(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl)
{ {
std::string temp = callsign; std::string temp = callsign;
temp.resize(LONG_CALLSIGN_LENGTH - 1U, ' '); temp.resize(LONG_CALLSIGN_LENGTH - 1U, ' ');
@ -67,12 +72,12 @@ void CAPRSWriter::setPort(const std::string& callsign, const std::string& band,
m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, latitude, longitude, agl); m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, latitude, longitude, agl);
} }
bool CAPRSWriter::open() bool CAPRSHandler::open()
{ {
return m_thread->start(); return m_thread->start();
} }
void CAPRSWriter::writeHeader(const std::string& callsign, const CHeaderData& header) void CAPRSHandler::writeHeader(const std::string& callsign, const CHeaderData& header)
{ {
CAPRSEntry* entry = m_array[callsign]; CAPRSEntry* entry = m_array[callsign];
if (entry == NULL) { if (entry == NULL) {
@ -87,7 +92,7 @@ void CAPRSWriter::writeHeader(const std::string& callsign, const CHeaderData& he
collector->writeHeader(header.getMyCall1()); collector->writeHeader(header.getMyCall1());
} }
void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) void CAPRSHandler::writeData(const std::string& callsign, const CAMBEData& data)
{ {
if (data.isEnd()) if (data.isEnd())
return; return;
@ -117,48 +122,31 @@ void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data)
return; return;
} }
// Check the transmission timer collector->getData([=](const std::string& text)
bool ok = entry->isOK(); {
if (!ok) { CAPRSFrame frame;
collector->reset(); if(!CAPRSParser::parseFrame(text, frame)) {
return; CLog::logWarning("Failed to parse DPRS Frame : %s", text.c_str());
} return;
}
unsigned int length = collector->getData(buffer, 400U);
std::string text((char*)buffer, length);
auto n = text.find(':');
if (n == std::string::npos) {
collector->reset();
return;
}
std::string header = text.substr(0, n);
std::string body = text.substr(n + 1U);
// If we already have a q-construct, don't send it on
n = header.find('q');
if (n != std::string::npos)
return;
// Remove the trailing \r
n = body.find('\r');
if (n != std::string::npos)
body = body.substr(0, n);
std::string output = CStringUtils::string_format("%s,qAR,%s-%s:%s", header.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), body.c_str());
char ascii[500U]; // If we already have a q-construct, don't send it on
::memset(ascii, 0x00, 500U); if(std::any_of(frame.getPath().begin(), frame.getPath().end(), [] (std::string s) { return !s.empty() && s[0] == 'q'; })) {
for (unsigned int i = 0U; i < output.length(); i++) CLog::logWarning("DPRS Frame already has q construct, not forwarding to APRS-IS: %s", text.c_str());
ascii[i] = output[i]; return;
}
m_thread->write(ascii); frame.getPath().push_back("qAR");
frame.getPath().push_back(CStringUtils::string_format("%s-%s", entry->getCallsign().c_str(), entry->getBand().c_str()));
std::string output ;
CAPRSFormater::frameToString(output, frame);
collector->reset(); m_thread->write(frame);
});
} }
void CAPRSWriter::writeStatus(const std::string& callsign, const std::string status) void CAPRSHandler::writeStatus(const std::string& callsign, const std::string status)
{ {
CAPRSEntry* entry = m_array[callsign]; CAPRSEntry* entry = m_array[callsign];
if (entry == NULL) { if (entry == NULL) {
@ -169,7 +157,7 @@ void CAPRSWriter::writeStatus(const std::string& callsign, const std::string sta
entry->getStatus().setStatus(status); entry->getStatus().setStatus(status);
} }
void CAPRSWriter::clock(unsigned int ms) void CAPRSHandler::clock(unsigned int ms)
{ {
m_thread->clock(ms); m_thread->clock(ms);
@ -189,48 +177,54 @@ void CAPRSWriter::clock(unsigned int ms)
} }
} }
void CAPRSWriter::sendStatusFrame(CAPRSEntry * entry) void CAPRSHandler::sendStatusFrame(CAPRSEntry * entry)
{ {
assert(entry != nullptr); assert(entry != nullptr);
if(!m_thread->isConnected()) if(!m_thread->isConnected())
return; return;
auto linkStatus = entry->getStatus(); auto linkStatus = entry->getStatus();
std::string body = boost::trim_copy(linkStatus.getStatus()); std::string body = boost::trim_copy(linkStatus.getStatus());
if(body[0] != '>') if(body[0] != '>')
body = '>' + body; body.insert(0, ">");
std::string output = CStringUtils::string_format("%s-%s>APD5T3,TCPIP*,qAC,%s-%sS:%s\r\n", std::string sourCall = entry->getCallsign() + '-' + entry->getBand();
entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(),
body.c_str()); CAPRSFrame frame(sourCall,
"APD5T3",
{ "TCPIP*", "qAC", sourCall + "S" },
body,
APFT_STATUS);
m_thread->write(output.c_str()); m_thread->write(frame);
} }
void CAPRSWriter::sendIdFrames() void CAPRSHandler::sendIdFrames()
{ {
if(m_thread->isConnected()) if(m_thread->isConnected())
{ {
for(auto entry : m_array) { for(auto entry : m_array) {
std::vector<std::string> frames; std::vector<CAPRSFrame *> frames;
if(m_idFrameProvider->buildAPRSFrames(m_gateway, entry.second, frames)) { if(m_idFrameProvider->buildAPRSFrames(m_gateway, entry.second, frames)) {
for(auto frame : frames) { for(auto frame : frames) {
m_thread->write(frame.c_str()); m_thread->write(*frame);
delete frame;
} }
} }
} }
} }
} }
bool CAPRSWriter::isConnected() const bool CAPRSHandler::isConnected() const
{ {
return m_thread->isConnected(); return m_thread->isConnected();
} }
void CAPRSWriter::close() void CAPRSHandler::close()
{ {
if(m_idFrameProvider != nullptr) { if(m_idFrameProvider != nullptr) {
m_idFrameProvider->close(); m_idFrameProvider->close();
@ -240,3 +234,8 @@ void CAPRSWriter::close()
m_thread->stop(); m_thread->stop();
} }
void CAPRSHandler::addReadAPRSCallback(IReadAPRSFrameCallback* cb)
{
m_thread->addReadAPRSCallback(cb);
}

@ -17,8 +17,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#ifndef APRSWriter_H #pragma once
#define APRSWriter_H
#include "Defs.h" #include "Defs.h"
@ -27,7 +26,7 @@
#include "APRSEntry.h" #include "APRSEntry.h"
#include "APRSWriterThread.h" #include "APRSHandlerThread.h"
#include "UDPReaderWriter.h" #include "UDPReaderWriter.h"
#include "APRSCollector.h" #include "APRSCollector.h"
#include "DStarDefines.h" #include "DStarDefines.h"
@ -36,10 +35,10 @@
#include "Timer.h" #include "Timer.h"
#include "APRSIdFrameProvider.h" #include "APRSIdFrameProvider.h"
class CAPRSWriter { class CAPRSHandler {
public: public:
CAPRSWriter(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address); CAPRSHandler(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address);
~CAPRSWriter(); ~CAPRSHandler();
bool open(); bool open();
@ -59,8 +58,10 @@ public:
void close(); void close();
void addReadAPRSCallback(IReadAPRSFrameCallback* cb);
private: private:
CAPRSWriterThread* m_thread; CAPRSHandlerThread* m_thread;
std::string m_gateway; std::string m_gateway;
in_addr m_address; in_addr m_address;
unsigned int m_port; unsigned int m_port;
@ -71,5 +72,3 @@ private:
void sendStatusFrame(CAPRSEntry * entrry); void sendStatusFrame(CAPRSEntry * entrry);
}; };
#endif

@ -21,18 +21,22 @@
#include <iostream> #include <iostream>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include "APRSWriterThread.h" #include "APRSHandlerThread.h"
#include "DStarDefines.h" #include "DStarDefines.h"
#include "Utils.h" #include "Utils.h"
#include "Defs.h" #include "Defs.h"
#include "Log.h" #include "Log.h"
#include "Version.h" #include "Version.h"
#include "APRSFormater.h"
#include "APRSParser.h"
// #define DUMP_TX // #define DUMP_TX
const unsigned int APRS_TIMEOUT = 10U; const unsigned int APRS_TIMEOUT = 10U;
const unsigned int APRS_READ_TIMEOUT = 1U;
const unsigned int APRS_KEEP_ALIVE_TIMEOUT = 60U;
CAPRSWriterThread::CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port) : CAPRSHandlerThread::CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port) :
CThread(), CThread(),
m_username(callsign), m_username(callsign),
m_password(password), m_password(password),
@ -42,9 +46,10 @@ m_queue(20U),
m_exit(false), m_exit(false),
m_connected(false), m_connected(false),
m_reconnectTimer(1000U), m_reconnectTimer(1000U),
m_keepAliveTimer(1000U, APRS_KEEP_ALIVE_TIMEOUT),
m_tries(0U), m_tries(0U),
m_APRSReadCallback(NULL), m_APRSReadCallbacks(),
m_filter(""), m_filter(),
m_clientName(FULL_PRODUCT_NAME) m_clientName(FULL_PRODUCT_NAME)
{ {
assert(!callsign.empty()); assert(!callsign.empty());
@ -59,7 +64,7 @@ m_clientName(FULL_PRODUCT_NAME)
m_ssid = m_ssid.substr(LONG_CALLSIGN_LENGTH - 1U, 1); m_ssid = m_ssid.substr(LONG_CALLSIGN_LENGTH - 1U, 1);
} }
CAPRSWriterThread::CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter, const std::string& clientName) : CAPRSHandlerThread::CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter) :
CThread(), CThread(),
m_username(callsign), m_username(callsign),
m_password(password), m_password(password),
@ -69,10 +74,11 @@ m_queue(20U),
m_exit(false), m_exit(false),
m_connected(false), m_connected(false),
m_reconnectTimer(1000U), m_reconnectTimer(1000U),
m_keepAliveTimer(1000U, APRS_KEEP_ALIVE_TIMEOUT),
m_tries(0U), m_tries(0U),
m_APRSReadCallback(NULL), m_APRSReadCallbacks(),
m_filter(filter), m_filter(filter),
m_clientName(clientName) m_clientName(FULL_PRODUCT_NAME)
{ {
assert(!callsign.empty()); assert(!callsign.empty());
assert(!password.empty()); assert(!password.empty());
@ -86,13 +92,20 @@ m_clientName(clientName)
m_ssid = m_ssid.substr(LONG_CALLSIGN_LENGTH - 1U, 1); m_ssid = m_ssid.substr(LONG_CALLSIGN_LENGTH - 1U, 1);
} }
CAPRSWriterThread::~CAPRSWriterThread() CAPRSHandlerThread::~CAPRSHandlerThread()
{ {
std::vector<IReadAPRSFrameCallback *> callBacksCopy;
callBacksCopy.assign(m_APRSReadCallbacks.begin(), m_APRSReadCallbacks.end());
m_APRSReadCallbacks.clear();
callBacksCopy.clear();
m_username.clear(); m_username.clear();
m_password.clear(); m_password.clear();
} }
bool CAPRSWriterThread::start() bool CAPRSHandlerThread::start()
{ {
Create(); Create();
Run(); Run();
@ -100,7 +113,7 @@ bool CAPRSWriterThread::start()
return true; return true;
} }
void* CAPRSWriterThread::Entry() void* CAPRSHandlerThread::Entry()
{ {
CLog::logInfo("Starting the APRS Writer thread"); CLog::logInfo("Starting the APRS Writer thread");
@ -110,17 +123,25 @@ void* CAPRSWriterThread::Entry()
startReconnectionTimer(); startReconnectionTimer();
} }
#ifndef DEBUG_DSTARGW
try { try {
#endif
m_keepAliveTimer.start();
while (!m_exit) { while (!m_exit) {
if (!m_connected) { if (!m_connected) {
Sleep(100U);
if (m_reconnectTimer.isRunning() && m_reconnectTimer.hasExpired()) { if (m_reconnectTimer.isRunning() && m_reconnectTimer.hasExpired()) {
m_reconnectTimer.stop(); m_reconnectTimer.stop();
CLog::logDebug("Trying to reconnect to the APRS server");
m_connected = connect(); m_connected = connect();
if (!m_connected) { if (!m_connected) {
CLog::logInfo("Reconnect attempt to the APRS server has failed"); CLog::logInfo("Reconnect attempt to the APRS server has failed");
startReconnectionTimer(); startReconnectionTimer();
} }
else {
m_keepAliveTimer.start();
}
} }
} }
@ -128,45 +149,43 @@ void* CAPRSWriterThread::Entry()
m_tries = 0U; m_tries = 0U;
if(!m_queue.empty()){ if(!m_queue.empty()){
char* p = m_queue.getData(); auto frameStr = m_queue.getData();
std::string text(p);
CLog::logInfo("APRS ==> %s", text.c_str());
::strcat(p, "\r\n"); CLog::logInfo("APRS ==> %s", frameStr.c_str());
bool ret = m_socket.write((unsigned char*)p, ::strlen(p)); bool ret = m_socket.writeLine(frameStr);
if (!ret) { if (!ret) {
m_connected = false; m_connected = false;
m_socket.close(); m_socket.close();
CLog::logInfo("Connection to the APRS thread has failed"); CLog::logInfo("Error when writing to the APRS server");
startReconnectionTimer(); startReconnectionTimer();
} }
delete[] p;
} }
{ {
std::string line; std::string line;
int length = m_socket.readLine(line, APRS_TIMEOUT); int length = m_socket.readLine(line, APRS_READ_TIMEOUT);
/*if (length == 0)
CLog::logWarning(("No response from the APRS server after %u seconds", APRS_TIMEOUT);*/
if (length < 0) { if (length < 0 || m_keepAliveTimer.hasExpired()) {
m_connected = false; m_connected = false;
m_socket.close(); m_socket.close();
CLog::logError("Error when reading from the APRS server"); CLog::logError("Error when reading from the APRS server");
startReconnectionTimer(); startReconnectionTimer();
} }
else if(length > 0 && line[0] == '#') {
if(length > 0 && line[0] != '#'//check if we have something and if that something is an APRS frame m_keepAliveTimer.start();
&& m_APRSReadCallback != NULL)//do we have someone wanting an APRS Frame? }
{ else if(line.length() > 0 && line[0] != '#') {
//CLog::logInfo("Received APRS Frame : ") + line); m_keepAliveTimer.start();
m_APRSReadCallback(std::string(line)); CLog::logDebug("APRS <== %s", line.c_str());
CAPRSFrame readFrame;
if(CAPRSParser::parseFrame(line, readFrame)) {
for(auto cb : m_APRSReadCallbacks) {
CAPRSFrame f(readFrame);
cb->readAPRSFrame(f);
}
}
} }
} }
} }
} }
@ -174,62 +193,69 @@ void* CAPRSWriterThread::Entry()
m_socket.close(); m_socket.close();
while (!m_queue.empty()) { while (!m_queue.empty()) {
char* p = m_queue.getData(); auto s = m_queue.getData();
delete[] p; s.clear();
} }
#ifndef DEBUG_DSTARGW
} }
catch (std::exception& e) { catch (std::exception& e) {
std::string message(e.what()); std::string message(e.what());
CLog::logInfo("Exception raised in the APRS Writer thread - \"%s\"", message.c_str()); CLog::logInfo("Exception raised in the APRS Writer thread - \"%s\"", message.c_str());
throw;
} }
catch (...) { catch (...) {
CLog::logInfo("Unknown exception raised in the APRS Writer thread"); CLog::logInfo("Unknown exception raised in the APRS Writer thread");
throw;
} }
#endif
CLog::logInfo("Stopping the APRS Writer thread"); CLog::logInfo("Stopping the APRS Writer thread");
return NULL; return NULL;
} }
void CAPRSWriterThread::setReadAPRSCallback(ReadAPRSFrameCallback cb) void CAPRSHandlerThread::addReadAPRSCallback(IReadAPRSFrameCallback * cb)
{ {
m_APRSReadCallback = cb; assert(cb != nullptr);
m_APRSReadCallbacks.push_back(cb);
} }
void CAPRSWriterThread::write(const char* data) void CAPRSHandlerThread::write(CAPRSFrame& frame)
{ {
assert(data != NULL);
if (!m_connected) if (!m_connected)
return; return;
unsigned int len = ::strlen(data); std::string frameString;
if(CAPRSFormater::frameToString(frameString, frame)) {
boost::trim_if(frameString, [] (char c) { return c == '\r' || c == '\n'; }); // trim all CRLF, we will add our own, just to make sure we get rid of any garbage that might come from slow data
CLog::logTrace("Queued APRS Frame : %s", frameString.c_str());
frameString.append("\r\n");
char* p = new char[len + 5U]; m_queue.addData(frameString);
::strcpy(p, data); }
m_queue.addData(p);
} }
bool CAPRSWriterThread::isConnected() const bool CAPRSHandlerThread::isConnected() const
{ {
return m_connected; return m_connected;
} }
void CAPRSWriterThread::stop() void CAPRSHandlerThread::stop()
{ {
m_exit = true; m_exit = true;
Wait(); Wait();
} }
void CAPRSWriterThread::clock(unsigned int ms) void CAPRSHandlerThread::clock(unsigned int ms)
{ {
m_reconnectTimer.clock(ms); m_reconnectTimer.clock(ms);
m_keepAliveTimer.clock(ms);
} }
bool CAPRSWriterThread::connect() bool CAPRSHandlerThread::connect()
{ {
m_socket.close();
bool ret = m_socket.open(); bool ret = m_socket.open();
if (!ret) if (!ret)
return false; return false;
@ -266,7 +292,7 @@ bool CAPRSWriterThread::connect()
return false; return false;
} }
if (length < 0) { if (length < 0) {
CLog::logInfo("Error when reading from the APRS server"); CLog::logInfo("Error when reading from the APRS server (connect)");
m_socket.close(); m_socket.close();
return false; return false;
} }
@ -278,13 +304,15 @@ bool CAPRSWriterThread::connect()
return true; return true;
} }
void CAPRSWriterThread::startReconnectionTimer() void CAPRSHandlerThread::startReconnectionTimer()
{ {
// Clamp at a ten minutes reconnect time // Clamp at a ten minutes reconnect time
m_tries++; m_tries++;
if (m_tries > 10U) if (m_tries > 10U)
m_tries = 10U; m_tries = 10U;
CLog::logDebug("Next APRS reconnection try in %u minute", m_tries);
m_reconnectTimer.setTimeout(m_tries * 60U); m_reconnectTimer.setTimeout(m_tries * 60U);
m_reconnectTimer.start(); m_reconnectTimer.start();
} }

@ -19,24 +19,27 @@
#ifndef APRSWriterThread_H #ifndef APRSWriterThread_H
#define APRSWriterThread_H #define APRSWriterThread_H
#include <vector>
#include "TCPReaderWriterClient.h" #include "TCPReaderWriterClient.h"
#include "RingBuffer.h" #include "RingBuffer.h"
#include "Timer.h" #include "Timer.h"
#include "Thread.h" #include "Thread.h"
#include "ReadAPRSFrameCallback.h"
#include "APRSFrame.h"
typedef void (*ReadAPRSFrameCallback)(const std::string&);
class CAPRSWriterThread : public CThread { class CAPRSHandlerThread : public CThread {
public: public:
CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port); CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port);
CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter, const std::string& clientName); CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter);
virtual ~CAPRSWriterThread(); virtual ~CAPRSHandlerThread();
bool start(); bool start();
bool isConnected() const; bool isConnected() const;
void write(const char* data); void write(CAPRSFrame& data);
void* Entry(); void* Entry();
@ -44,19 +47,20 @@ public:
void clock(unsigned int ms); void clock(unsigned int ms);
void setReadAPRSCallback(ReadAPRSFrameCallback cb); void addReadAPRSCallback(IReadAPRSFrameCallback* cb);
private: private:
std::string m_username; std::string m_username;
std::string m_password; std::string m_password;
std::string m_ssid; std::string m_ssid;
CTCPReaderWriterClient m_socket; CTCPReaderWriterClient m_socket;
CRingBuffer<char*> m_queue; CRingBuffer<std::string> m_queue;
bool m_exit; bool m_exit;
bool m_connected; bool m_connected;
CTimer m_reconnectTimer; CTimer m_reconnectTimer;
CTimer m_keepAliveTimer;
unsigned int m_tries; unsigned int m_tries;
ReadAPRSFrameCallback m_APRSReadCallback; std::vector<IReadAPRSFrameCallback *> m_APRSReadCallbacks;
std::string m_filter; std::string m_filter;
std::string m_clientName; std::string m_clientName;

@ -31,7 +31,7 @@ CAPRSIdFrameProvider::~CAPRSIdFrameProvider()
} }
bool CAPRSIdFrameProvider::buildAPRSFrames(const std::string& gateway, const CAPRSEntry * entry, std::vector<std::string> & frames) bool CAPRSIdFrameProvider::buildAPRSFrames(const std::string& gateway, const CAPRSEntry * entry, std::vector<CAPRSFrame *> & frames)
{ {
assert(entry != nullptr); assert(entry != nullptr);

@ -22,6 +22,7 @@
#include "Timer.h" #include "Timer.h"
#include "APRSEntry.h" #include "APRSEntry.h"
#include "APRSFrame.h"
class CAPRSIdFrameProvider class CAPRSIdFrameProvider
{ {
@ -29,14 +30,14 @@ public:
CAPRSIdFrameProvider(unsigned int timeOut); CAPRSIdFrameProvider(unsigned int timeOut);
virtual ~CAPRSIdFrameProvider(); virtual ~CAPRSIdFrameProvider();
bool buildAPRSFrames(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector<std::string>& frames); bool buildAPRSFrames(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector<CAPRSFrame *>& frames);
void clock(unsigned int ms) { m_timer.clock(ms); } void clock(unsigned int ms) { m_timer.clock(ms); }
bool wantsToSend(); bool wantsToSend();
virtual void start() { }; virtual void start() { };
virtual void close() { }; virtual void close() { };
protected: protected:
virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector<std::string>& frames) = 0; virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector<CAPRSFrame *>& frames) = 0;
void setTimeout(unsigned int timeout) void setTimeout(unsigned int timeout)
{ {

@ -0,0 +1,120 @@
/*
* 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 <boost/algorithm/string.hpp>
#include "APRSUnit.h"
#include "APRSFormater.h"
#include "StringUtils.h"
#include "APRStoDPRS.h"
CAPRSUnit::CAPRSUnit(IRepeaterCallback * repeaterHandler) :
m_frameBuffer(20U),
m_status(APS_IDLE),
m_repeaterHandler(repeaterHandler),
m_headerData(nullptr),
m_slowData(nullptr),
m_out(0U),
m_seq(0U),
m_totalNeeded(0U),
m_timer(1000U, 2U),
m_start()
{
m_timer.start();
}
void CAPRSUnit::writeFrame(CAPRSFrame& frame)
{
auto frameCopy = new CAPRSFrame(frame);
frameCopy->getPath().clear();//path is of no use for us, just clear it
m_frameBuffer.push_back(frameCopy);
m_timer.start();
}
void CAPRSUnit::clock(unsigned int ms)
{
m_timer.clock(ms);
if(m_status == APS_IDLE && !m_frameBuffer.empty() && m_timer.hasExpired()) {
auto frame = m_frameBuffer.front();
m_frameBuffer.pop_front();
m_headerData = new CHeaderData();
std::string dprs, text;
if(!CAPRSToDPRS::aprsToDPRS(dprs, text, *m_headerData, *frame)) {
delete m_headerData;
m_headerData = nullptr;
return;
}
m_slowData = new CSlowDataEncoder();
m_slowData->setHeaderData(*m_headerData);
m_slowData->setGPSData(dprs);
m_slowData->setTextData(text);
m_totalNeeded = (m_slowData->getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U;
m_repeaterHandler->process(*m_headerData, DIR_INCOMING, AS_INFO);
m_out = 0U;
m_seq = 0U;
m_start = std::chrono::high_resolution_clock::now();
m_status = APS_TRANSMIT;
return;
}
if(m_status == APS_TRANSMIT) {
unsigned int needed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start).count();
needed /= DSTAR_FRAME_TIME_MS;
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
while (m_out < needed && m_out < m_totalNeeded) {
CAMBEData data;
data.setId(m_headerData->getId());
data.setSeq(m_seq);
if(m_out == m_totalNeeded - 1U)
data.setEnd(true);
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
// Insert sync bytes when the sequence number is zero, slow data otherwise
if (m_seq == 0U) {
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
} else {
m_slowData->getInterleavedData(buffer + VOICE_FRAME_LENGTH_BYTES);
m_out++;
}
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
m_repeaterHandler->process(data, DIR_INCOMING, AS_INFO);
m_seq++;
if (m_seq == 21U) m_seq = 0U;
}
if(m_out >= m_totalNeeded) {
m_status = APS_IDLE;
delete m_headerData;
delete m_slowData;
}
}
}

@ -0,0 +1,57 @@
/*
* 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 <string>
#include <boost/circular_buffer.hpp>
#include <chrono>
#include "APRSFrame.h"
#include "RepeaterCallback.h"
#include "Timer.h"
#include "SlowDataEncoder.h"
enum APRSUNIT_STATUS {
APS_IDLE,
APS_WAIT,
APS_TRANSMIT
};
class CAPRSUnit
{
public:
CAPRSUnit(IRepeaterCallback * repeaterHandler);
void writeFrame(CAPRSFrame& aprsFrame);
void clock(unsigned ms);
private:
// CRingBuffer<CAPRSFrame *> m_frameBuffer;
boost::circular_buffer<CAPRSFrame *> m_frameBuffer;
APRSUNIT_STATUS m_status;
IRepeaterCallback * m_repeaterHandler;
CHeaderData * m_headerData;
CSlowDataEncoder * m_slowData;
unsigned int m_out;
unsigned int m_seq;
unsigned int m_totalNeeded;
CTimer m_timer;
std::chrono::high_resolution_clock::time_point m_start;
};

@ -0,0 +1,73 @@
/*
* 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 <boost/algorithm/string.hpp>
#include "APRStoDPRS.h"
#include "Log.h"
#include "RSMS1AMessageBuilder.h"
bool CAPRSToDPRS::aprsToDPRS(std::string& dprs, std::string& text, CHeaderData& header, CAPRSFrame& frame)
{
dprs.clear();
text.clear();
switch (frame.getType())
{
case APFT_MESSAGE :
return messageToDPRS(dprs, text, header, frame);
default:
break;
}
return false;
}
bool CAPRSToDPRS::messageToDPRS(std::string& dprs, std::string& text, CHeaderData& header, CAPRSFrame& frame)
{
auto frameBody = frame.getBody();
if(frameBody.length() < 11 || frameBody[0] != ':' || frameBody[10] != ':') {
CLog::logDebug("Invalid APRS message body : %s", frameBody.c_str());
return false;
}
// extract recipient
auto recipient = boost::trim_copy(frameBody.substr(1, 9));
if(recipient.empty()) {
CLog::logDebug("APRS message has no recipient");
return false;
}
auto dashPos = recipient.find_first_of('-');
if(dashPos != std::string::npos)
recipient = recipient.substr(0, dashPos);
//extract message body
auto messageBody = boost::trim_copy(frameBody.substr(11));
header.setId(header.createId());
header.setMyCall1(frame.getSource());
header.setMyCall2("MSG");
header.setYourCall(recipient);
CRSMS1AMessageBuilder::buildMessage(dprs, frame.getSource(), recipient, messageBody);
text = messageBody;
return true;
}

@ -0,0 +1,34 @@
/*
* 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 <string>
#include <utility>
#include "HeaderData.h"
#include "APRSFrame.h"
class CAPRSToDPRS
{
public:
static bool aprsToDPRS(std::string& dprs, std::string& text, CHeaderData& header, CAPRSFrame& frame);
private:
static bool messageToDPRS(std::string& dprs, std::string& text, CHeaderData& header, CAPRSFrame& frame);
};

@ -44,6 +44,10 @@ CDCSProtocolHandlerPool::~CDCSProtocolHandlerPool()
CDCSProtocolHandler *CDCSProtocolHandlerPool::getIncomingHandler() CDCSProtocolHandler *CDCSProtocolHandlerPool::getIncomingHandler()
{ {
auto it = m_pool.find(m_basePort);
if(it != m_pool.end())
return it->second;
return getHandler(m_basePort); return getHandler(m_basePort);
} }

@ -403,8 +403,9 @@ void CDExtraHandler::process(CConnectData& connect)
} }
} }
void CDExtraHandler::link(IReflectorCallback* handler, const std::string& repeater, const std::string &gateway, const in_addr& address) void CDExtraHandler::link(IReflectorCallback* handler, const std::string& repeater, const std::string &gateway, const in_addr& address, unsigned int& localPort)
{ {
localPort = 0U;
CDExtraProtocolHandler* protoHandler = m_pool->getHandler(); CDExtraProtocolHandler* protoHandler = m_pool->getHandler();
if (protoHandler == NULL) if (protoHandler == NULL)
return; return;
@ -422,6 +423,7 @@ void CDExtraHandler::link(IReflectorCallback* handler, const std::string& repeat
} }
if (found) { if (found) {
localPort = protoHandler->getPort();
CConnectData reply(repeater, gateway, CT_LINK1, address, DEXTRA_PORT); CConnectData reply(repeater, gateway, CT_LINK1, address, DEXTRA_PORT);
protoHandler->writeConnect(reply); protoHandler->writeConnect(reply);
} else { } else {

@ -52,7 +52,7 @@ public:
static void setHeaderLogger(CHeaderLogger* logger); static void setHeaderLogger(CHeaderLogger* logger);
static void setMaxDongles(unsigned int maxDongles); static void setMaxDongles(unsigned int maxDongles);
static void link(IReflectorCallback* handler, const std::string& repeater, const std::string& reflector, const in_addr& address); static void link(IReflectorCallback* handler, const std::string& repeater, const std::string& reflector, const in_addr& address, unsigned int& localPort);
static void unlink(IReflectorCallback* handler, const std::string& reflector = "", bool exclude = true); static void unlink(IReflectorCallback* handler, const std::string& reflector = "", bool exclude = true);
static void unlink(); static void unlink();

@ -19,7 +19,7 @@
*/ */
#include "DExtraProtocolHandler.h" #include "DExtraProtocolHandler.h"
#include "Log.h"
#include "Utils.h" #include "Utils.h"
// #define DUMP_TX // #define DUMP_TX
@ -113,6 +113,17 @@ bool CDExtraProtocolHandler::writeConnect(const CConnectData& connect)
return true; return true;
} }
void CDExtraProtocolHandler::traverseNat(const std::string& address, unsigned int remotePort)
{
unsigned char buffer = 0x00U;
in_addr addr = CUDPReaderWriter::lookup(address);
CLog::logInfo("DExtra Punching hole to %s:%u", address.c_str(), remotePort);
m_socket.write(&buffer, 1U, addr, remotePort);
}
DEXTRA_TYPE CDExtraProtocolHandler::read() DEXTRA_TYPE CDExtraProtocolHandler::read()
{ {
bool res = true; bool res = true;

@ -51,6 +51,7 @@ public:
bool writeAMBE(const CAMBEData& data); bool writeAMBE(const CAMBEData& data);
bool writeConnect(const CConnectData& connect); bool writeConnect(const CConnectData& connect);
bool writePoll(const CPollData& poll); bool writePoll(const CPollData& poll);
void traverseNat(const std::string& address, unsigned int remotePort);
DEXTRA_TYPE read(); DEXTRA_TYPE read();
CHeaderData* readHeader(); CHeaderData* readHeader();

@ -43,6 +43,10 @@ CDExtraProtocolHandlerPool::~CDExtraProtocolHandlerPool()
CDExtraProtocolHandler* CDExtraProtocolHandlerPool::getIncomingHandler() CDExtraProtocolHandler* CDExtraProtocolHandlerPool::getIncomingHandler()
{ {
auto it = m_pool.find(m_basePort);
if(it != m_pool.end())
return it->second;
return getHandler(m_basePort); return getHandler(m_basePort);
} }

@ -72,7 +72,9 @@ void* CDPlusAuthenticator::Entry()
m_timer.start(); m_timer.start();
#ifndef DEBUG_DSTARGW
try { try {
#endif
while (!m_killed) { while (!m_killed) {
if (m_timer.hasExpired()) { if (m_timer.hasExpired()) {
authenticate(m_loginCallsign, OPENDSTAR_HOSTNAME, OPENDSTAR_PORT, '2', true); authenticate(m_loginCallsign, OPENDSTAR_HOSTNAME, OPENDSTAR_PORT, '2', true);
@ -83,14 +85,18 @@ void* CDPlusAuthenticator::Entry()
m_timer.clock(); m_timer.clock();
} }
#ifndef DEBUG_DSTARGW
} }
catch (std::exception& e) { catch (std::exception& e) {
std::string message(e.what()); std::string message(e.what());
CLog::logError("Exception raised in the D-Plus Authenticator thread - \"%s\"", message.c_str()); CLog::logError("Exception raised in the D-Plus Authenticator thread - \"%s\"", message.c_str());
throw;
} }
catch (...) { catch (...) {
CLog::logError("Unknown exception raised in the D-Plus Authenticator thread"); CLog::logError("Unknown exception raised in the D-Plus Authenticator thread");
throw;
} }
#endif
CLog::logInfo("Stopping the D-Plus Authenticator thread"); CLog::logInfo("Stopping the D-Plus Authenticator thread");

@ -357,8 +357,9 @@ void CDPlusHandler::process(CConnectData& connect)
} }
} }
void CDPlusHandler::link(IReflectorCallback* handler, const std::string& repeater, const std::string &gateway, const in_addr& address) void CDPlusHandler::link(IReflectorCallback* handler, const std::string& repeater, const std::string &gateway, const in_addr& address, unsigned int& localPort)
{ {
localPort = 0U;
CDPlusProtocolHandler* protoHandler = m_pool->getHandler(); CDPlusProtocolHandler* protoHandler = m_pool->getHandler();
if (protoHandler == NULL) if (protoHandler == NULL)
return; return;
@ -377,6 +378,7 @@ void CDPlusHandler::link(IReflectorCallback* handler, const std::string& repeate
if (found) { if (found) {
CConnectData connect(CT_LINK1, address, DPLUS_PORT); CConnectData connect(CT_LINK1, address, DPLUS_PORT);
localPort = protoHandler->getPort();
protoHandler->writeConnect(connect); protoHandler->writeConnect(connect);
m_stateChange = true; m_stateChange = true;
} else { } else {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save

Powered by TurnKey Linux.