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
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
@ -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);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue