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