From 0cc9f43bb8e3ba4c290d161ec965327d3899c6b6 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 3 Jan 2022 22:34:01 +0100 Subject: [PATCH 01/59] #12 Add formater and start using it --- APRSFormater.cpp | 54 +++++++++++++ APRSFormater.h | 27 +++++++ APRSFrame.cpp | 4 +- APRSFrame.h | 4 +- APRSParser.cpp | 33 ++++++-- APRSParser.h | 1 + APRSWriter.cpp | 28 ++++--- StringUtils.h | 3 + Tests/APRSFormater/frameToString.cpp | 115 +++++++++++++++++++++++++++ Tests/APRSParser/parseAPRSFrame.cpp | 33 +++++++- 10 files changed, 278 insertions(+), 24 deletions(-) create mode 100644 APRSFormater.cpp create mode 100644 APRSFormater.h create mode 100644 Tests/APRSFormater/frameToString.cpp diff --git a/APRSFormater.cpp b/APRSFormater.cpp new file mode 100644 index 0000000..ecd1f68 --- /dev/null +++ b/APRSFormater.cpp @@ -0,0 +1,54 @@ +/* + * 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 + +#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; + } + + // std::string path(frame.getPath().size() > 0U ? "," : ""); + // for(auto pathItem : frame.getPath()) { + // path.append(boost::trim_copy(pathItem)).push_back(','); + // } + + // boost::trim_right_if(path, [](char c){ return c == ','; }); + + 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; +} + diff --git a/APRSFormater.h b/APRSFormater.h new file mode 100644 index 0000000..c42bf0c --- /dev/null +++ b/APRSFormater.h @@ -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); +}; \ No newline at end of file diff --git a/APRSFrame.cpp b/APRSFrame.cpp index f5e29e7..1f8724f 100644 --- a/APRSFrame.cpp +++ b/APRSFrame.cpp @@ -22,15 +22,17 @@ 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& path, APRS_FRAME_TYPE type) : +CAPRSFrame::CAPRSFrame(const std::string& source, const std::string& destination, const std::vector& 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()); diff --git a/APRSFrame.h b/APRSFrame.h index d84a048..c1b9137 100644 --- a/APRSFrame.h +++ b/APRSFrame.h @@ -25,12 +25,14 @@ enum APRS_FRAME_TYPE { APFT_UNKNOWN = 0, APFT_MESSAGE, + APFT_POSITION, + APFT_NMEA, }; class CAPRSFrame { public: CAPRSFrame(); - CAPRSFrame(const std::string& source, const std::string& destination, const std::vector& path, APRS_FRAME_TYPE type); + CAPRSFrame(const std::string& source, const std::string& destination, const std::vector& path, const std::string& body, APRS_FRAME_TYPE type); void clear(); std::string& getSource() { return m_source; } diff --git a/APRSParser.cpp b/APRSParser.cpp index 4663b79..b5ed46d 100644 --- a/APRSParser.cpp +++ b/APRSParser.cpp @@ -20,9 +20,13 @@ #include "Log.h" bool CAPRSParser::parseFrame(const std::string& frameStr, CAPRSFrame& frame) +{ + return parseFrame(frameStr, frame, false); +} +bool CAPRSParser::parseFrame(const std::string& frameStr, CAPRSFrame& frame, bool doNotEnforceFrameType) { frame.clear(); - bool ret = false; + bool ret = doNotEnforceFrameType; if(!frameStr.empty()) { auto pos = frameStr.find_first_of(':'); @@ -44,12 +48,14 @@ bool CAPRSParser::parseFrame(const std::string& frameStr, CAPRSFrame& frame) frame.getBody().assign(body); - setFrameType(frame); - if(frame.getType() == APFT_UNKNOWN) { - CLog::logInfo("Invalid or unsupported APRS frame : %s", frameStr); - } - else { - ret = true; + if(!doNotEnforceFrameType) { + setFrameType(frame); + if(frame.getType() == APFT_UNKNOWN) { + CLog::logInfo("Invalid or unsupported APRS frame : %s", frameStr); + } + else { + ret = true; + } } } } @@ -71,7 +77,18 @@ void CAPRSParser::setFrameType(CAPRSFrame& frame) [](char c){ return c == ' ' || c == '-' || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); })) type = APFT_MESSAGE; break; - + // case '!': + // if(body.length() >= 2U && body[1] == '!') { + // // This is ultimeter 200 weather station + // type = APFT_UNKNOWN; + // break; + // } else { + // type = APFT_POSITION; + // } + // case '=': + // case '/': + // case '@': + // break; default: break; } diff --git a/APRSParser.h b/APRSParser.h index 6d94f09..89d4b7e 100644 --- a/APRSParser.h +++ b/APRSParser.h @@ -27,6 +27,7 @@ class CAPRSParser { public: static bool parseFrame(const std::string& frameStr, CAPRSFrame& frame); + static bool parseFrame(const std::string& frameStr, CAPRSFrame& frame, bool doNotEnforceFrameType); private: static void setFrameType(CAPRSFrame& frame); diff --git a/APRSWriter.cpp b/APRSWriter.cpp index 9af5bc9..a301af5 100644 --- a/APRSWriter.cpp +++ b/APRSWriter.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "StringUtils.h" #include "Log.h" @@ -28,6 +29,9 @@ #include "DStarDefines.h" #include "Defs.h" #include "Log.h" +#include "APRSFrame.h" +#include "APRSParser.h" +#include "APRSFormater.h" CAPRSWriter::CAPRSWriter(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address) : m_thread(NULL), @@ -127,26 +131,24 @@ void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) unsigned int length = collector->getData(SLOW_DATA_TYPE_GPS, buffer, 400U); std::string text((char*)buffer, length); - auto n = text.find(':'); - if (n == std::string::npos) { + CAPRSFrame frame; + if(!CAPRSParser::parseFrame(text, frame, true)) { collector->reset(); + CLog::logWarning("Failed to parse DPRS Frame : %s", text.c_str()); return; } - std::string header = text.substr(0, n); - std::string body = text.substr(n + 1U); - // If we already have a q-construct, don't send it on - n = header.find('q'); - if (n != std::string::npos) + if(std::any_of(frame.getPath().begin(), frame.getPath().end(), [] (std::string s) { return !s.empty() && s[0] == 'q'; })) { + CLog::logWarning("DPRS Frame already has q construct, not forwarding to APRS-IS: %s", text.c_str()); return; + } - // Remove the trailing \r - n = body.find('\r'); - if (n != std::string::npos) - body = body.substr(0, n); - - std::string output = CStringUtils::string_format("%s,qAR,%s-%s:%s", header.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), body.c_str()); + frame.getPath().push_back("qAR"); + frame.getPath().push_back(CStringUtils::string_format("%s-%s", entry->getCallsign().c_str(), entry->getBand().c_str())); + + std::string output ; + CAPRSFormater::frameToString(output, frame); char ascii[500U]; ::memset(ascii, 0x00, 500U); diff --git a/StringUtils.h b/StringUtils.h index 55853b9..ccb7294 100644 --- a/StringUtils.h +++ b/StringUtils.h @@ -21,8 +21,11 @@ #include #include #include +#include #define string_right(s,l) (s.substr(s.length() - l, l)) +#define string_is_blank(s) (std::all_of(s.begin(), s.end(), [](char c) { return c == ' '; })) +#define string_is_blank_or_empty(s) (s.empty() || std::all_of(s.begin(), s.end(), [](char c) { return c == ' '; })) class CStringUtils { public: diff --git a/Tests/APRSFormater/frameToString.cpp b/Tests/APRSFormater/frameToString.cpp new file mode 100644 index 0000000..7b457e4 --- /dev/null +++ b/Tests/APRSFormater/frameToString.cpp @@ -0,0 +1,115 @@ +/* + * 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 + +#include "../../APRSFormater.h" + + class APRSFormater_frameToString : public ::testing::Test { + +}; + +TEST_F(APRSFormater_frameToString, EmptyFrame) { + + CAPRSFrame aprsFrame("", "", { }, "", APFT_UNKNOWN); + std::string output("This should be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STREQ(output.c_str(), "This should be left untouched if the test succeeds"); +} + +TEST_F(APRSFormater_frameToString, SourceOnly) { + + CAPRSFrame aprsFrame("N0CALL", "", { }, "", APFT_UNKNOWN); + std::string output("This should be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STREQ(output.c_str(), "This should be left untouched if the test succeeds"); +} + +TEST_F(APRSFormater_frameToString, DestinationOnly) { + + CAPRSFrame aprsFrame("", "APRS", { }, "", APFT_UNKNOWN); + std::string output("This should be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STREQ(output.c_str(), "This should be left untouched if the test succeeds"); +} + +TEST_F(APRSFormater_frameToString, PathOnly) { + + CAPRSFrame aprsFrame("", "", { "WIDE1-1, WIDE2-1" }, "", APFT_UNKNOWN); + std::string output("This should be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STREQ(output.c_str(), "This should be left untouched if the test succeeds"); +} + +TEST_F(APRSFormater_frameToString, BodyOnly) { + + CAPRSFrame aprsFrame("", "", { }, "Lorem Ipsum", APFT_UNKNOWN); + std::string output("This should be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STREQ(output.c_str(), "This should be left untouched if the test succeeds"); +} + +TEST_F(APRSFormater_frameToString, CorrectWithoutPath) { + + CAPRSFrame aprsFrame("N0CALL", "APRS", { }, "Lorem Ipsum", APFT_UNKNOWN); + std::string output("This should NOT be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STREQ(output.c_str(), "N0CALL>APRS:Lorem Ipsum"); +} + +TEST_F(APRSFormater_frameToString, CorrectWithPath) { + + CAPRSFrame aprsFrame("N0CALL", "APRS", { "WIDE1-1", "WIDE2-2" }, "Lorem Ipsum", APFT_UNKNOWN); + std::string output("This should be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STREQ(output.c_str(), "N0CALL>APRS,WIDE1-1,WIDE2-2:Lorem Ipsum"); +} + +TEST_F(APRSFormater_frameToString, WithSomeEmptyPath) { + + CAPRSFrame aprsFrame("N0CALL", "APRS", { "WIDE1-1", "", "WIDE2-2" }, "Lorem Ipsum", APFT_UNKNOWN); + std::string output("This should be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STREQ(output.c_str(), "N0CALL>APRS,WIDE1-1,WIDE2-2:Lorem Ipsum"); +} + +TEST_F(APRSFormater_frameToString, WithSomeBlankPath) { + + CAPRSFrame aprsFrame("N0CALL", "APRS", { "WIDE1-1", "", "WIDE2-2" }, "Lorem Ipsum", APFT_UNKNOWN); + std::string output("This should be left untouched if the test succeeds"); + bool retVal = CAPRSFormater::frameToString(output, aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STREQ(output.c_str(), "N0CALL>APRS,WIDE1-1,WIDE2-2:Lorem Ipsum"); +} \ No newline at end of file diff --git a/Tests/APRSParser/parseAPRSFrame.cpp b/Tests/APRSParser/parseAPRSFrame.cpp index 50c2bd1..2c0dd71 100644 --- a/Tests/APRSParser/parseAPRSFrame.cpp +++ b/Tests/APRSParser/parseAPRSFrame.cpp @@ -91,7 +91,7 @@ TEST_F(APRSParser_parseAPRSFrame, CorrectMessageFrameWithoutDigipeater) { EXPECT_EQ(aprsFrame.getPath().size(), 0); } -TEST_F(APRSParser_parseAPRSFrame, InvalideMessageFrame) { +TEST_F(APRSParser_parseAPRSFrame, InvalidMessageFrame) { CAPRSFrame aprsFrame; bool retVal = CAPRSParser::parseFrame("N0CALL>APRS::F4ABC&@#$:Test Message", aprsFrame); @@ -102,4 +102,35 @@ TEST_F(APRSParser_parseAPRSFrame, InvalideMessageFrame) { EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); EXPECT_EQ(aprsFrame.getPath().size(), 0U); +} + +TEST_F(APRSParser_parseAPRSFrame, ValidFrameDoNotEnforceType) { + + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("N0CALL>APRS,WIDE1-1,WIDE2-2:Lorem Ipsum", aprsFrame, true); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "Lorem Ipsum"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "N0CALL"); + EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); + EXPECT_EQ(aprsFrame.getPath().size(), 2); + EXPECT_STREQ(aprsFrame.getPath()[0].c_str(), "WIDE1-1"); + EXPECT_STREQ(aprsFrame.getPath()[1].c_str(), "WIDE2-2"); +} + +// + +TEST_F(APRSParser_parseAPRSFrame, ID51) { + + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("F4FXL-8>API51,DSTAR:!1234.51N/12345.42E[/A=000886QRV DStar\r\r\n", aprsFrame, true); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "!4849.51N/00736.42E[/A=000886QRV DStar\r\r\n"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "API51"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F4FXL-8"); + EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); + EXPECT_EQ(aprsFrame.getPath().size(), 1); + EXPECT_STREQ(aprsFrame.getPath()[0].c_str(), "DSTAR"); } \ No newline at end of file From e7c1abafd76e61be7109fdb319b387cd67ae880b Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 3 Jan 2022 22:48:28 +0100 Subject: [PATCH 02/59] Fix test --- Tests/APRSParser/parseAPRSFrame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/APRSParser/parseAPRSFrame.cpp b/Tests/APRSParser/parseAPRSFrame.cpp index 2c0dd71..4f48e82 100644 --- a/Tests/APRSParser/parseAPRSFrame.cpp +++ b/Tests/APRSParser/parseAPRSFrame.cpp @@ -124,10 +124,10 @@ TEST_F(APRSParser_parseAPRSFrame, ValidFrameDoNotEnforceType) { TEST_F(APRSParser_parseAPRSFrame, ID51) { CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("F4FXL-8>API51,DSTAR:!1234.51N/12345.42E[/A=000886QRV DStar\r\r\n", aprsFrame, true); + bool retVal = CAPRSParser::parseFrame("F4FXL-8>API51,DSTAR:!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n", aprsFrame, true); EXPECT_TRUE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "!4849.51N/00736.42E[/A=000886QRV DStar\r\r\n"); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n"); EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "API51"); EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F4FXL-8"); EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); From 9a9790f30ee4a87bca07d85bc063959a97fc8f72 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 4 Jan 2022 20:19:53 +0100 Subject: [PATCH 03/59] #13 clean up --- SlowDataCollector.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/SlowDataCollector.cpp b/SlowDataCollector.cpp index b651ae7..004226e 100644 --- a/SlowDataCollector.cpp +++ b/SlowDataCollector.cpp @@ -69,24 +69,6 @@ bool CSlowDataCollector::writeData(const unsigned char* data) break; } - // unsigned char rxDataType = (m_buffer[0] & SLOW_DATA_TYPE_MASK); - - // switch (rxDataType) - // { - // case SLOW_DATA_TYPE_MASK: CLog::logDebug("SLOW_DATA_TYPE_MASK "); break; - // case SLOW_DATA_TYPE_GPS: CLog::logDebug("SLOW_DATA_TYPE_GPS "); break; - // case SLOW_DATA_TYPE_TEXT: CLog::logDebug("SLOW_DATA_TYPE_TEXT "); break; - // case SLOW_DATA_TYPE_HEADER: CLog::logDebug("SLOW_DATA_TYPE_HEADER "); break; - // case SLOW_DATA_TYPE_MESSAGE: CLog::logDebug("SLOW_DATA_TYPE_MESSAGE "); break; - // case SLOW_DATA_TYPE_FAST_DATA1: CLog::logDebug("SLOW_DATA_TYPE_FAST_DATA1 "); break; - // case SLOW_DATA_TYPE_FAST_DATA2: CLog::logDebug("SLOW_DATA_TYPE_FAST_DATA2 "); break; - // case SLOW_DATA_TYPE_SQUELCH: CLog::logDebug("SLOW_DATA_TYPE_SQUELCH "); break; - // case SLOW_DATA_LENGTH_MASK: CLog::logDebug("SLOW_DATA_LENGTH_MASK "); break; - // default: - // CLog::logDebug("!!!!!!!!!!!!!!! %X", rxDataType); - // break; - // }; - if((m_buffer[0] & SLOW_DATA_TYPE_MASK) == m_slowDataType) return addData(m_buffer + 1U); From 8cf946e4e9e58b5abb139ea68c9470e67f647e8d Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 16:52:36 +0100 Subject: [PATCH 04/59] Always build with GPS d --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e148e64..c0f8117 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,10 +21,10 @@ jobs: sudo apt-get -y install libgtest-dev libcurl4-openssl-dev libboost-dev - run: name: "Build App" - command: "make -j 3 dstargateway" + command: "make -j 3 dstargateway USE_GPSD=1" - run: name: "Build Tests" - command: "make -j 3 tests" + command: "make -j 3 tests USE_GPSD=1" - run: name: "Run Tests" command: "make run-tests" From afc3d11621f9e42d71deab777ec3611ef4bcc50a Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 16:54:45 +0100 Subject: [PATCH 05/59] also install libgps as prerequisite --- .vscode/tasks.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2fa8b11..f50a809 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -10,7 +10,10 @@ "args": [ "-j3" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] }, { @@ -21,10 +24,7 @@ "-j3", "tests" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] } ] From c9ac1e7a51e2065c11e29b3daa196787959ea58d Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 16:56:21 +0100 Subject: [PATCH 06/59] Add libgps-dev --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c0f8117..ac19a68 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,7 +18,7 @@ jobs: name: Install dependencies command: | sudo apt-get update - sudo apt-get -y install libgtest-dev libcurl4-openssl-dev libboost-dev + sudo apt-get -y install libgtest-dev libcurl4-openssl-dev libboost-dev libgps-dev - run: name: "Build App" command: "make -j 3 dstargateway USE_GPSD=1" From 525dccec6cf6b5be824d2e81b8cb5e78a2f4c440 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 17:46:27 +0100 Subject: [PATCH 07/59] Change task to build with GPSD --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f50a809..74133c5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,7 +8,7 @@ "type": "shell", "command": "make", "args": [ - "-j3" + "-j3", "USE_GPSD=1" ], "group": { "kind": "build", From 0b6d9e560e9aff697e49c975bda0c15e0b72df36 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 17:47:07 +0100 Subject: [PATCH 08/59] #12 Improve Writeline --- TCPReaderWriterClient.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/TCPReaderWriterClient.cpp b/TCPReaderWriterClient.cpp index e21da7a..624cb51 100644 --- a/TCPReaderWriterClient.cpp +++ b/TCPReaderWriterClient.cpp @@ -201,19 +201,15 @@ bool CTCPReaderWriterClient::write(const unsigned char* buffer, unsigned int len bool CTCPReaderWriterClient::writeLine(const std::string& line) { - std::string lineCopy(line); - if(lineCopy.size() > 0 && lineCopy.at(lineCopy.size() - 1) != '\n') - lineCopy.push_back('\n'); - - //stupidly write one char after the other - size_t len = lineCopy.size(); - bool result = true; - for(size_t i = 0; i < len && result; i++){ - unsigned char c = lineCopy.at(i); - result = write(&c , 1); - } + if(line.empty()) + return true; + + bool ret = write((unsigned char *)line.c_str(), line.length()); + + if(line[line.length() - 1] != '\n')//make sure we send a newline + ret = writeLine("\n") && ret; - return result; + return ret; } void CTCPReaderWriterClient::close() From 59f7e51701c953909f4b743450a4457597b3a71d Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 17:47:24 +0100 Subject: [PATCH 09/59] #13 use APRSFrame everywhere --- APRSFixedIdFrameProvider.cpp | 23 ++++++++++++++------- APRSFixedIdFrameProvider.h | 2 +- APRSFrame.h | 2 ++ APRSGPSDIdFrameProvider.cpp | 36 ++++++++++++++++++--------------- APRSGPSDIdFrameProvider.h | 2 +- APRSIdFrameProvider.cpp | 2 +- APRSIdFrameProvider.h | 5 +++-- APRSWriter.cpp | 28 ++++++++++++++------------ APRSWriterThread.cpp | 39 ++++++++++++++++++------------------ APRSWriterThread.h | 5 +++-- ReadAPRSFrameCallback.h | 4 +++- RingBuffer.h | 2 -- 12 files changed, 85 insertions(+), 65 deletions(-) diff --git a/APRSFixedIdFrameProvider.cpp b/APRSFixedIdFrameProvider.cpp index c75cbe9..9434175 100644 --- a/APRSFixedIdFrameProvider.cpp +++ b/APRSFixedIdFrameProvider.cpp @@ -29,7 +29,7 @@ CAPRSIdFrameProvider(20U) // Initial timeout of 20 seconds } -bool CAPRSFixedIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector& frames) +bool CAPRSFixedIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector& frames) { if (entry == nullptr) return false; @@ -107,24 +107,33 @@ bool CAPRSFixedIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, c boost::replace_all(lat, ",", "."); boost::replace_all(lon, ",", "."); - std::string output = CStringUtils::string_format("%s-S>APD5T1,TCPIP*,qAC,%s-GS:;%-7s%-2s*%02d%02d%02dz%s%cD%s%caRNG%04.0lf/A=%06.0lf %s %s\r\n", - gateway.c_str(), gateway.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), + std::string body = CStringUtils::string_format(";%-7s%-2s*%02d%02d%02dz%s%cD%s%caRNG%04.0lf/A=%06.0lf %s %s\r\n", + entry->getCallsign().c_str(), entry->getBand().c_str(), tm->tm_mday, tm->tm_hour, tm->tm_min, lat.c_str(), (entry->getLatitude() < 0.0F) ? 'S' : 'N', lon.c_str(), (entry->getLongitude() < 0.0F) ? 'W' : 'E', entry->getRange() * 0.6214, entry->getAGL() * 3.28, band.c_str(), desc.c_str()); + CAPRSFrame * frame = new CAPRSFrame(gateway + "-S", + "APD5T1", + { "TCPIP*", "qAC" , gateway + "-GS" }, + body, APFT_OBJECT); - frames.push_back(output); + frames.push_back(frame); if (entry->getBand().length() == 1U) { - output = CStringUtils::string_format("%s-%s>APD5T2,TCPIP*,qAC,%s-%sS:!%s%cD%s%c&RNG%04.0lf/A=%06.0lf %s %s\r\n", - entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), + body = CStringUtils::string_format("!%s%cD%s%c&RNG%04.0lf/A=%06.0lf %s %s\r\n", lat.c_str(), (entry->getLatitude() < 0.0F) ? 'S' : 'N', lon.c_str(), (entry->getLongitude() < 0.0F) ? 'W' : 'E', entry->getRange() * 0.6214, entry->getAGL() * 3.28, band.c_str(), desc.c_str()); - frames.push_back(output); + frame = new CAPRSFrame(entry->getCallsign() + "-" + entry->getBand(), + "APD5T2", + { "TCPIP*", "qAC", entry->getCallsign() + "-" + entry->getBand() + "S"}, + body, APFT_POSITION); + + + frames.push_back(frame); } setTimeout(20U * 60U);//20 minutes, plenty enough for fixed diff --git a/APRSFixedIdFrameProvider.h b/APRSFixedIdFrameProvider.h index d3bbc3d..8fa7032 100644 --- a/APRSFixedIdFrameProvider.h +++ b/APRSFixedIdFrameProvider.h @@ -26,5 +26,5 @@ public: CAPRSFixedIdFrameProvider(); protected: - virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames); + virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames); }; diff --git a/APRSFrame.h b/APRSFrame.h index c1b9137..6105821 100644 --- a/APRSFrame.h +++ b/APRSFrame.h @@ -27,6 +27,8 @@ enum APRS_FRAME_TYPE { APFT_MESSAGE, APFT_POSITION, APFT_NMEA, + APFT_STATUS, + APFT_OBJECT }; class CAPRSFrame { diff --git a/APRSGPSDIdFrameProvider.cpp b/APRSGPSDIdFrameProvider.cpp index 3a02916..f67f634 100644 --- a/APRSGPSDIdFrameProvider.cpp +++ b/APRSGPSDIdFrameProvider.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifdef USE_DGPS +#ifdef USE_GPSD #include #include @@ -56,7 +56,7 @@ void CAPRSGPSDIdFrameProvider::close() } } -bool CAPRSGPSDIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector& frames) +bool CAPRSGPSDIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector& frames) { if(!m_hasConnection) { this->start(); @@ -169,40 +169,44 @@ bool CAPRSGPSDIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, co boost::replace_all(lat, ",", "."); boost::replace_all(lon, ",", "."); - std::string output1 = CStringUtils::string_format("%s-S>APD5T1,TCPIP*,qAC,%s-GS:;%-7s%-2s*%02d%02d%02dz%s%cD%s%ca/A=%06.0lf", - gateway.c_str(), gateway.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), + std::string body = CStringUtils::string_format(";%-7s%-2s*%02d%02d%02dz%s%cD%s%ca/A=%06.0lf", + entry->getCallsign().c_str(), entry->getBand().c_str(), tm->tm_mday, tm->tm_hour, tm->tm_min, lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N', lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E', rawAltitude * 3.28); - std::string output2; if (bearingSet && velocitySet) - output2 = CStringUtils::string_format("%03.0lf/%03.0lf", rawBearing, rawVelocity * 0.539957F); + body.append(CStringUtils::string_format("%03.0lf/%03.0lf", rawBearing, rawVelocity * 0.539957F)); - std::string output3; - output3 = CStringUtils::string_format("RNG%04.0lf %s %s\r\n", entry->getRange() * 0.6214, band.c_str(), desc.c_str()); + body.append(CStringUtils::string_format("RNG%04.0lf %s %s\r\n", entry->getRange() * 0.6214, band.c_str(), desc.c_str())); - CLog::logDebug("APRS ==> %s%s%s", output1.c_str(), output2.c_str(), output3.c_str()); - frames.push_back(output1.append(output2).append(output3)); + CAPRSFrame * frame = new CAPRSFrame(gateway + "-S", + "APD5T1", + { "TCPIP*", "qAC" , gateway + "-GS" }, + body, APFT_OBJECT); + + + frames.push_back(frame); if (entry->getBand().length() == 1U) { if (altitudeSet) - output1 = CStringUtils::string_format("%s-%s>APD5T2,TCPIP*,qAC,%s-%sS:!%s%cD%s%c&/A=%06.0lf", - entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), + body = CStringUtils::string_format("%s%cD%s%c&/A=%06.0lf", lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N', lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E', rawAltitude * 3.28); else - output1 = CStringUtils::string_format("%s-%s>APD5T2,TCPIP*,qAC,%s-%sS:!%s%cD%s%c&", - entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), + body = CStringUtils::string_format("!%s%cD%s%c&", lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N', lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E'); - CLog::logDebug("APRS ==> %s%s%s", output1.c_str(), output2.c_str(), output3.c_str()); + frame = new CAPRSFrame(gateway, + "APD5T2", + { "TCPIP*", "qAC" , gateway + "-GS" }, + body, APFT_POSITION); - frames.push_back(output1.append(output2).append(output3)); + frames.push_back(frame); } setTimeout(60U * 5U);//5 Minutes is plenty enough we aint an APRS tracker ! diff --git a/APRSGPSDIdFrameProvider.h b/APRSGPSDIdFrameProvider.h index 77ca4b9..e982f9b 100644 --- a/APRSGPSDIdFrameProvider.h +++ b/APRSGPSDIdFrameProvider.h @@ -35,7 +35,7 @@ public: virtual void close(); protected: - virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames); + virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames); private: std::string m_gpsdAddress; diff --git a/APRSIdFrameProvider.cpp b/APRSIdFrameProvider.cpp index 503720a..3637f9d 100644 --- a/APRSIdFrameProvider.cpp +++ b/APRSIdFrameProvider.cpp @@ -31,7 +31,7 @@ CAPRSIdFrameProvider::~CAPRSIdFrameProvider() } -bool CAPRSIdFrameProvider::buildAPRSFrames(const std::string& gateway, const CAPRSEntry * entry, std::vector & frames) +bool CAPRSIdFrameProvider::buildAPRSFrames(const std::string& gateway, const CAPRSEntry * entry, std::vector & frames) { assert(entry != nullptr); diff --git a/APRSIdFrameProvider.h b/APRSIdFrameProvider.h index f83deb7..48917ee 100644 --- a/APRSIdFrameProvider.h +++ b/APRSIdFrameProvider.h @@ -22,6 +22,7 @@ #include "Timer.h" #include "APRSEntry.h" +#include "APRSFrame.h" class CAPRSIdFrameProvider { @@ -29,14 +30,14 @@ public: CAPRSIdFrameProvider(unsigned int timeOut); virtual ~CAPRSIdFrameProvider(); - bool buildAPRSFrames(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames); + bool buildAPRSFrames(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames); void clock(unsigned int ms) { m_timer.clock(ms); } bool wantsToSend(); virtual void start() { }; virtual void close() { }; protected: - virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames) = 0; + virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames) = 0; void setTimeout(unsigned int timeout) { diff --git a/APRSWriter.cpp b/APRSWriter.cpp index a301af5..cc122ee 100644 --- a/APRSWriter.cpp +++ b/APRSWriter.cpp @@ -32,6 +32,7 @@ #include "APRSFrame.h" #include "APRSParser.h" #include "APRSFormater.h" +#include "APRSUtils.h" CAPRSWriter::CAPRSWriter(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address) : m_thread(NULL), @@ -150,12 +151,7 @@ void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) std::string output ; CAPRSFormater::frameToString(output, frame); - char ascii[500U]; - ::memset(ascii, 0x00, 500U); - for (unsigned int i = 0U; i < output.length(); i++) - ascii[i] = output[i]; - - m_thread->write(ascii); + m_thread->write(frame); collector->reset(); } @@ -198,17 +194,22 @@ void CAPRSWriter::sendStatusFrame(CAPRSEntry * entry) if(!m_thread->isConnected()) return; + auto linkStatus = entry->getStatus(); std::string body = boost::trim_copy(linkStatus.getStatus()); if(body[0] != '>') - body = '>' + body; + body.insert(0, ">"); - std::string output = CStringUtils::string_format("%s-%s>APD5T3,TCPIP*,qAC,%s-%sS:%s\r\n", - entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), - body.c_str()); + std::string sourCall = entry->getCallsign() + '-' + entry->getBand(); + + CAPRSFrame frame(sourCall, + "APD5T3", + { "TCPIP*", "qAC", sourCall + "S" }, + linkStatus.getStatus(), + APFT_STATUS); - m_thread->write(output.c_str()); + m_thread->write(frame); } @@ -217,10 +218,11 @@ void CAPRSWriter::sendIdFrames() if(m_thread->isConnected()) { for(auto entry : m_array) { - std::vector frames; + std::vector frames; if(m_idFrameProvider->buildAPRSFrames(m_gateway, entry.second, frames)) { for(auto frame : frames) { - m_thread->write(frame.c_str()); + m_thread->write(*frame); + delete frame; } } } diff --git a/APRSWriterThread.cpp b/APRSWriterThread.cpp index aa2462b..c029957 100644 --- a/APRSWriterThread.cpp +++ b/APRSWriterThread.cpp @@ -27,6 +27,8 @@ #include "Defs.h" #include "Log.h" #include "Version.h" +#include "APRSFormater.h" +#include "APRSParser.h" // #define DUMP_TX @@ -139,22 +141,17 @@ void* CAPRSWriterThread::Entry() m_tries = 0U; if(!m_queue.empty()){ - char* p = m_queue.getData(); + auto frameStr = m_queue.getData(); - std::string text(p); - CLog::logInfo("APRS ==> %s", text.c_str()); + CLog::logInfo("APRS ==> %s", frameStr.c_str()); - ::strcat(p, "\r\n"); - - bool ret = m_socket.write((unsigned char*)p, ::strlen(p)); + bool ret = m_socket.writeLine(frameStr); if (!ret) { m_connected = false; m_socket.close(); CLog::logInfo("Connection to the APRS thread has failed"); startReconnectionTimer(); } - - delete[] p; } { std::string line; @@ -176,8 +173,12 @@ void* CAPRSWriterThread::Entry() if(length > 0 && line[0] != '#'//check if we have something and if that something is an APRS frame && m_APRSReadCallback.size() > 0U)//do we have someone wanting an APRS Frame? { - for(auto cb : m_APRSReadCallback) { - cb->readAprsFrame(line); + CAPRSFrame readFrame; + if(CAPRSParser::parseFrame(line, readFrame)) { + for(auto cb : m_APRSReadCallback) { + CAPRSFrame f(readFrame); + cb->readAprsFrame(f); + } } } } @@ -189,8 +190,8 @@ void* CAPRSWriterThread::Entry() m_socket.close(); while (!m_queue.empty()) { - char* p = m_queue.getData(); - delete[] p; + auto s = m_queue.getData(); + s.clear(); } } catch (std::exception& e) { @@ -212,19 +213,19 @@ void CAPRSWriterThread::addReadAPRSCallback(CReadAPRSFrameCallback * cb) m_APRSReadCallback.push_back(cb); } -void CAPRSWriterThread::write(const char* data) +void CAPRSWriterThread::write(CAPRSFrame& frame) { - assert(data != NULL); - if (!m_connected) return; - unsigned int len = ::strlen(data); + std::string frameString; + if(CAPRSFormater::frameToString(frameString, frame)) { - char* p = new char[len + 5U]; - ::strcpy(p, data); + boost::trim_if(frameString, [] (char c) { return c == '\r' || c == '\n'; }); // trim all CRLF, we will add our own, just to make sure we get rid of any garbage that might come from slow data + frameString.append("\r\n"); - m_queue.addData(p); + m_queue.addData(frameString); + } } bool CAPRSWriterThread::isConnected() const diff --git a/APRSWriterThread.h b/APRSWriterThread.h index 2e21f9e..bc58ba4 100644 --- a/APRSWriterThread.h +++ b/APRSWriterThread.h @@ -26,6 +26,7 @@ #include "Timer.h" #include "Thread.h" #include "ReadAPRSFrameCallback.h" +#include "APRSFrame.h" class CAPRSWriterThread : public CThread { @@ -38,7 +39,7 @@ public: bool isConnected() const; - void write(const char* data); + void write(CAPRSFrame& data); void* Entry(); @@ -53,7 +54,7 @@ private: std::string m_password; std::string m_ssid; CTCPReaderWriterClient m_socket; - CRingBuffer m_queue; + CRingBuffer m_queue; bool m_exit; bool m_connected; CTimer m_reconnectTimer; diff --git a/ReadAPRSFrameCallback.h b/ReadAPRSFrameCallback.h index 1b02dc3..9a93193 100644 --- a/ReadAPRSFrameCallback.h +++ b/ReadAPRSFrameCallback.h @@ -20,9 +20,11 @@ #include +#include "APRSFrame.h" + class CReadAPRSFrameCallback { public: virtual ~CReadAPRSFrameCallback(){ } - virtual bool readAprsFrame(const std::string& aprsFrame) = 0; + virtual bool readAprsFrame(CAPRSFrame& aprsFrame) = 0; }; diff --git a/RingBuffer.h b/RingBuffer.h index 3182382..1ff2ab4 100644 --- a/RingBuffer.h +++ b/RingBuffer.h @@ -36,8 +36,6 @@ public: assert(length > 0U); m_buffer = new T[length]; - - ::memset(m_buffer, 0x00, length * sizeof(T)); } ~CRingBuffer() From c3dfa420974290b590a05c60d513aa9c122b3821 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 17:52:47 +0100 Subject: [PATCH 10/59] #12 fix invalid status frame --- APRSWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/APRSWriter.cpp b/APRSWriter.cpp index cc122ee..bfb948e 100644 --- a/APRSWriter.cpp +++ b/APRSWriter.cpp @@ -206,7 +206,7 @@ void CAPRSWriter::sendStatusFrame(CAPRSEntry * entry) CAPRSFrame frame(sourCall, "APD5T3", { "TCPIP*", "qAC", sourCall + "S" }, - linkStatus.getStatus(), + body, APFT_STATUS); m_thread->write(frame); From 84be5d84439464e571108395d254b5d105abd053 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 17:57:27 +0100 Subject: [PATCH 11/59] Also run tests with gpsd --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ac19a68..0502fb1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,7 +27,7 @@ jobs: command: "make -j 3 tests USE_GPSD=1" - run: name: "Run Tests" - command: "make run-tests" + command: "make run-tests USE_GPSD=1" # Invoke jobs via workflows # See: https://circleci.com/docs/2.0/configuration-reference/#workflows workflows: From 5e1cfb4d2c8c8e325a909aac35ebe5762de2f421 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 20:10:45 +0100 Subject: [PATCH 12/59] remove newline --- NMEASentenceCollector.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NMEASentenceCollector.cpp b/NMEASentenceCollector.cpp index 2685625..b368060 100644 --- a/NMEASentenceCollector.cpp +++ b/NMEASentenceCollector.cpp @@ -86,8 +86,7 @@ unsigned int CNMEASentenceCollector::getDataInt(unsigned char * data, unsigned i CAPRSUtils::dstarCallsignToAPRS(fromCall); std::string aprsFrame(fromCall); aprsFrame.append("-5>GPS30,DSTAR*:") - .append(nmea) - .append("\r\n"); + .append(nmea); auto aprsFrameLen = aprsFrame.length(); if(length < aprsFrameLen) { From 6a7cf1a8df2c78492fa3b9b3c66c6573a082c8d9 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 20:11:18 +0100 Subject: [PATCH 13/59] #12 add some more frame types --- .vscode/tasks.json | 6 +- APRSFrame.h | 3 +- APRSParser.cpp | 125 +++++++++++++++++++--------- APRSParser.h | 5 +- APRSWriter.cpp | 2 +- Tests/APRSParser/parseAPRSFrame.cpp | 19 +---- 6 files changed, 96 insertions(+), 64 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 74133c5..2dc1e81 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,7 +8,8 @@ "type": "shell", "command": "make", "args": [ - "-j3", "USE_GPSD=1" + "-j3", + "USE_GPSD=1" ], "group": { "kind": "build", @@ -22,7 +23,8 @@ "command": "make", "args": [ "-j3", - "tests" + "tests", + "USE_GPSD=1" ], "group": "build", "problemMatcher": [] diff --git a/APRSFrame.h b/APRSFrame.h index 6105821..6c50eee 100644 --- a/APRSFrame.h +++ b/APRSFrame.h @@ -28,7 +28,8 @@ enum APRS_FRAME_TYPE { APFT_POSITION, APFT_NMEA, APFT_STATUS, - APFT_OBJECT + APFT_OBJECT, + APFT_WX }; class CAPRSFrame { diff --git a/APRSParser.cpp b/APRSParser.cpp index b5ed46d..cdd7b5a 100644 --- a/APRSParser.cpp +++ b/APRSParser.cpp @@ -21,12 +21,8 @@ bool CAPRSParser::parseFrame(const std::string& frameStr, CAPRSFrame& frame) { - return parseFrame(frameStr, frame, false); -} -bool CAPRSParser::parseFrame(const std::string& frameStr, CAPRSFrame& frame, bool doNotEnforceFrameType) -{ frame.clear(); - bool ret = doNotEnforceFrameType; + bool ret = false; if(!frameStr.empty()) { auto pos = frameStr.find_first_of(':'); @@ -48,14 +44,9 @@ bool CAPRSParser::parseFrame(const std::string& frameStr, CAPRSFrame& frame, boo frame.getBody().assign(body); - if(!doNotEnforceFrameType) { - setFrameType(frame); - if(frame.getType() == APFT_UNKNOWN) { - CLog::logInfo("Invalid or unsupported APRS frame : %s", frameStr); - } - else { - ret = true; - } + ret = parseInt(frame); + if(!ret) { + frame.clear(); } } } @@ -64,37 +55,89 @@ bool CAPRSParser::parseFrame(const std::string& frameStr, CAPRSFrame& frame, boo return ret; } -void CAPRSParser::setFrameType(CAPRSFrame& frame) +bool CAPRSParser::parseInt(CAPRSFrame& frame) { APRS_FRAME_TYPE type = APFT_UNKNOWN; - std::string body(frame.getBody()); + 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; - if(!body.empty()) { - switch (body[0]) - { - case ':': - if(body[10] == ':' && std::all_of(body.begin() + 1, body.begin() + 10, - [](char c){ return c == ' ' || c == '-' || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); })) - type = APFT_MESSAGE; - break; - // case '!': - // if(body.length() >= 2U && body[1] == '!') { - // // This is ultimeter 200 weather station - // type = APFT_UNKNOWN; - // break; - // } else { - // type = APFT_POSITION; - // } - // case '=': - // case '/': - // case '@': - // break; - default: - break; - } - } + 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 ':': + 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; + 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; + default: + type = APFT_UNKNOWN; + break; + } + frame.getType() = type; - if(type == APFT_UNKNOWN) - frame.clear(); + 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] */ } \ No newline at end of file diff --git a/APRSParser.h b/APRSParser.h index 89d4b7e..6414ffb 100644 --- a/APRSParser.h +++ b/APRSParser.h @@ -27,8 +27,9 @@ class CAPRSParser { public: static bool parseFrame(const std::string& frameStr, CAPRSFrame& frame); - static bool parseFrame(const std::string& frameStr, CAPRSFrame& frame, bool doNotEnforceFrameType); private: - static void setFrameType(CAPRSFrame& frame); + static bool parseInt(CAPRSFrame& frame); + static bool valid_sym_table_compressed(unsigned char c); + static bool valid_sym_table_uncompressed(unsigned char c); }; \ No newline at end of file diff --git a/APRSWriter.cpp b/APRSWriter.cpp index bfb948e..4e2d1ce 100644 --- a/APRSWriter.cpp +++ b/APRSWriter.cpp @@ -133,7 +133,7 @@ void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) std::string text((char*)buffer, length); CAPRSFrame frame; - if(!CAPRSParser::parseFrame(text, frame, true)) { + if(!CAPRSParser::parseFrame(text, frame)) { collector->reset(); CLog::logWarning("Failed to parse DPRS Frame : %s", text.c_str()); return; diff --git a/Tests/APRSParser/parseAPRSFrame.cpp b/Tests/APRSParser/parseAPRSFrame.cpp index 4f48e82..0263348 100644 --- a/Tests/APRSParser/parseAPRSFrame.cpp +++ b/Tests/APRSParser/parseAPRSFrame.cpp @@ -104,33 +104,18 @@ TEST_F(APRSParser_parseAPRSFrame, InvalidMessageFrame) { EXPECT_EQ(aprsFrame.getPath().size(), 0U); } -TEST_F(APRSParser_parseAPRSFrame, ValidFrameDoNotEnforceType) { - - CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("N0CALL>APRS,WIDE1-1,WIDE2-2:Lorem Ipsum", aprsFrame, true); - - EXPECT_TRUE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "Lorem Ipsum"); - EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); - EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "N0CALL"); - EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); - EXPECT_EQ(aprsFrame.getPath().size(), 2); - EXPECT_STREQ(aprsFrame.getPath()[0].c_str(), "WIDE1-1"); - EXPECT_STREQ(aprsFrame.getPath()[1].c_str(), "WIDE2-2"); -} - // TEST_F(APRSParser_parseAPRSFrame, ID51) { CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("F4FXL-8>API51,DSTAR:!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n", aprsFrame, true); + bool retVal = CAPRSParser::parseFrame("F4FXL-8>API51,DSTAR:!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n", aprsFrame); EXPECT_TRUE(retVal); EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n"); EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "API51"); EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F4FXL-8"); - EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); + EXPECT_EQ(aprsFrame.getType(), APFT_POSITION); EXPECT_EQ(aprsFrame.getPath().size(), 1); EXPECT_STREQ(aprsFrame.getPath()[0].c_str(), "DSTAR"); } \ No newline at end of file From 6fae94217b04c2fef8f79ada5baba82858ce1297 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 20:15:06 +0100 Subject: [PATCH 14/59] #12 rename variable --- APRSWriterThread.cpp | 14 +++++++------- APRSWriterThread.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/APRSWriterThread.cpp b/APRSWriterThread.cpp index c029957..6b39111 100644 --- a/APRSWriterThread.cpp +++ b/APRSWriterThread.cpp @@ -45,7 +45,7 @@ m_exit(false), m_connected(false), m_reconnectTimer(1000U), m_tries(0U), -m_APRSReadCallback(), +m_APRSReadCallbacks(), m_filter(""), m_clientName(FULL_PRODUCT_NAME) { @@ -72,7 +72,7 @@ m_exit(false), m_connected(false), m_reconnectTimer(1000U), m_tries(0U), -m_APRSReadCallback(), +m_APRSReadCallbacks(), m_filter(filter), m_clientName(clientName) { @@ -91,9 +91,9 @@ m_clientName(clientName) CAPRSWriterThread::~CAPRSWriterThread() { std::vector callBacksCopy; - callBacksCopy.assign(m_APRSReadCallback.begin(), m_APRSReadCallback.end()); + callBacksCopy.assign(m_APRSReadCallbacks.begin(), m_APRSReadCallbacks.end()); - m_APRSReadCallback.clear(); + m_APRSReadCallbacks.clear(); for(auto cb : callBacksCopy) { delete cb; @@ -171,11 +171,11 @@ void* CAPRSWriterThread::Entry() CLog::logDebug("Received APRS Frame : %s", line.c_str()); if(length > 0 && line[0] != '#'//check if we have something and if that something is an APRS frame - && m_APRSReadCallback.size() > 0U)//do we have someone wanting an APRS Frame? + && m_APRSReadCallbacks.size() > 0U)//do we have someone wanting an APRS Frame? { CAPRSFrame readFrame; if(CAPRSParser::parseFrame(line, readFrame)) { - for(auto cb : m_APRSReadCallback) { + for(auto cb : m_APRSReadCallbacks) { CAPRSFrame f(readFrame); cb->readAprsFrame(f); } @@ -210,7 +210,7 @@ void* CAPRSWriterThread::Entry() void CAPRSWriterThread::addReadAPRSCallback(CReadAPRSFrameCallback * cb) { assert(cb != nullptr); - m_APRSReadCallback.push_back(cb); + m_APRSReadCallbacks.push_back(cb); } void CAPRSWriterThread::write(CAPRSFrame& frame) diff --git a/APRSWriterThread.h b/APRSWriterThread.h index bc58ba4..e76b20f 100644 --- a/APRSWriterThread.h +++ b/APRSWriterThread.h @@ -59,7 +59,7 @@ private: bool m_connected; CTimer m_reconnectTimer; unsigned int m_tries; - std::vector m_APRSReadCallback; + std::vector m_APRSReadCallbacks; std::string m_filter; std::string m_clientName; From c56c42d28375c1e8ff0029827a745e09a993ec69 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 5 Jan 2022 20:32:01 +0100 Subject: [PATCH 15/59] #13 rename APRSWriter -> APRSHandler --- APRSWriter.cpp => APRSHandler.cpp | 26 +++++++++++++------------- APRSWriter.h => APRSHandler.h | 11 ++++------- DStarGatewayApp.cpp | 4 ++-- DStarGatewayThread.cpp | 2 +- DStarGatewayThread.h | 6 +++--- RepeaterHandler.cpp | 4 ++-- RepeaterHandler.h | 6 +++--- 7 files changed, 28 insertions(+), 31 deletions(-) rename APRSWriter.cpp => APRSHandler.cpp (85%) rename APRSWriter.h => APRSHandler.h (89%) diff --git a/APRSWriter.cpp b/APRSHandler.cpp similarity index 85% rename from APRSWriter.cpp rename to APRSHandler.cpp index 4e2d1ce..c36e470 100644 --- a/APRSWriter.cpp +++ b/APRSHandler.cpp @@ -25,7 +25,7 @@ #include "StringUtils.h" #include "Log.h" -#include "APRSWriter.h" +#include "APRSHandler.h" #include "DStarDefines.h" #include "Defs.h" #include "Log.h" @@ -34,7 +34,7 @@ #include "APRSFormater.h" #include "APRSUtils.h" -CAPRSWriter::CAPRSWriter(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address) : +CAPRSHandler::CAPRSHandler(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address) : m_thread(NULL), m_gateway(), m_address(), @@ -54,7 +54,7 @@ m_idFrameProvider(nullptr) boost::trim(m_gateway); } -CAPRSWriter::~CAPRSWriter() +CAPRSHandler::~CAPRSHandler() { for(auto it = m_array.begin(); it != m_array.end(); it++) { delete it->second; @@ -63,7 +63,7 @@ CAPRSWriter::~CAPRSWriter() m_array.clear(); } -void CAPRSWriter::setPort(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl) +void CAPRSHandler::setPort(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl) { std::string temp = callsign; temp.resize(LONG_CALLSIGN_LENGTH - 1U, ' '); @@ -72,12 +72,12 @@ void CAPRSWriter::setPort(const std::string& callsign, const std::string& band, m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, latitude, longitude, agl); } -bool CAPRSWriter::open() +bool CAPRSHandler::open() { return m_thread->start(); } -void CAPRSWriter::writeHeader(const std::string& callsign, const CHeaderData& header) +void CAPRSHandler::writeHeader(const std::string& callsign, const CHeaderData& header) { CAPRSEntry* entry = m_array[callsign]; if (entry == NULL) { @@ -92,7 +92,7 @@ void CAPRSWriter::writeHeader(const std::string& callsign, const CHeaderData& he collector->writeHeader(header.getMyCall1()); } -void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) +void CAPRSHandler::writeData(const std::string& callsign, const CAMBEData& data) { if (data.isEnd()) return; @@ -156,7 +156,7 @@ void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) collector->reset(); } -void CAPRSWriter::writeStatus(const std::string& callsign, const std::string status) +void CAPRSHandler::writeStatus(const std::string& callsign, const std::string status) { CAPRSEntry* entry = m_array[callsign]; if (entry == NULL) { @@ -167,7 +167,7 @@ void CAPRSWriter::writeStatus(const std::string& callsign, const std::string sta entry->getStatus().setStatus(status); } -void CAPRSWriter::clock(unsigned int ms) +void CAPRSHandler::clock(unsigned int ms) { m_thread->clock(ms); @@ -187,7 +187,7 @@ void CAPRSWriter::clock(unsigned int ms) } } -void CAPRSWriter::sendStatusFrame(CAPRSEntry * entry) +void CAPRSHandler::sendStatusFrame(CAPRSEntry * entry) { assert(entry != nullptr); @@ -213,7 +213,7 @@ void CAPRSWriter::sendStatusFrame(CAPRSEntry * entry) } -void CAPRSWriter::sendIdFrames() +void CAPRSHandler::sendIdFrames() { if(m_thread->isConnected()) { @@ -229,12 +229,12 @@ void CAPRSWriter::sendIdFrames() } } -bool CAPRSWriter::isConnected() const +bool CAPRSHandler::isConnected() const { return m_thread->isConnected(); } -void CAPRSWriter::close() +void CAPRSHandler::close() { if(m_idFrameProvider != nullptr) { m_idFrameProvider->close(); diff --git a/APRSWriter.h b/APRSHandler.h similarity index 89% rename from APRSWriter.h rename to APRSHandler.h index 3543930..9cfa671 100644 --- a/APRSWriter.h +++ b/APRSHandler.h @@ -17,8 +17,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef APRSWriter_H -#define APRSWriter_H +#pragma once #include "Defs.h" @@ -36,10 +35,10 @@ #include "Timer.h" #include "APRSIdFrameProvider.h" -class CAPRSWriter { +class CAPRSHandler { public: - CAPRSWriter(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address); - ~CAPRSWriter(); + CAPRSHandler(const std::string& hostname, unsigned int port, const std::string& gateway, const std::string& password, const std::string& address); + ~CAPRSHandler(); bool open(); @@ -71,5 +70,3 @@ private: void sendStatusFrame(CAPRSEntry * entrry); }; -#endif - diff --git a/DStarGatewayApp.cpp b/DStarGatewayApp.cpp index 37e520a..3a8ae42 100644 --- a/DStarGatewayApp.cpp +++ b/DStarGatewayApp.cpp @@ -133,9 +133,9 @@ bool CDStarGatewayApp::createThread() // Setup APRS TAPRS aprsConfig; config.getAPRS(aprsConfig); - CAPRSWriter * aprsWriter = NULL; + CAPRSHandler * aprsWriter = NULL; if(aprsConfig.enabled && !aprsConfig.password.empty()) { - aprsWriter = new CAPRSWriter(aprsConfig.hostname, aprsConfig.port, gatewayConfig.callsign, aprsConfig.password, gatewayConfig.address); + aprsWriter = new CAPRSHandler(aprsConfig.hostname, aprsConfig.port, gatewayConfig.callsign, aprsConfig.password, gatewayConfig.address); if(aprsWriter->open()) { #ifdef USE_GPSD CAPRSIdFrameProvider * idFrameProvider = aprsConfig.m_positionSource == POSSRC_GPSD ? (CAPRSIdFrameProvider *)new CAPRSGPSDIdFrameProvider(gpsdConfig.m_address, gpsdConfig.m_port) diff --git a/DStarGatewayThread.cpp b/DStarGatewayThread.cpp index 23aa7a2..8c12a6f 100644 --- a/DStarGatewayThread.cpp +++ b/DStarGatewayThread.cpp @@ -638,7 +638,7 @@ void CDStarGatewayThread::setLog(bool enabled) m_logEnabled = enabled; } -void CDStarGatewayThread::setAPRSWriter(CAPRSWriter* writer) +void CDStarGatewayThread::setAPRSWriter(CAPRSHandler* writer) { m_aprsWriter = writer; } diff --git a/DStarGatewayThread.h b/DStarGatewayThread.h index 6b5d912..d90dd26 100644 --- a/DStarGatewayThread.h +++ b/DStarGatewayThread.h @@ -32,7 +32,7 @@ #include "RemoteHandler.h" #include "CacheManager.h" #include "CallsignList.h" -#include "APRSWriter.h" +#include "APRSHandler.h" #include "IRCDDB.h" #include "Timer.h" #include "Defs.h" @@ -72,7 +72,7 @@ virtual void addRepeater(const std::string& callsign, const std::string& band, c virtual void setCCS(bool enabled, const std::string& host); #endif virtual void setLog(bool enabled); - virtual void setAPRSWriter(CAPRSWriter* writer); + virtual void setAPRSWriter(CAPRSHandler* writer); virtual void setInfoEnabled(bool enabled); virtual void setEchoEnabled(bool enabled); virtual void setDTMFEnabled(bool enabled); @@ -109,7 +109,7 @@ private: #if defined(ENABLE_NAT_TRAVERSAL) CNatTraversalHandler* m_natTraversal; #endif - CAPRSWriter* m_aprsWriter; + CAPRSHandler* m_aprsWriter; CIRCDDB* m_irc; CCacheManager m_cache; TEXT_LANG m_language; diff --git a/RepeaterHandler.cpp b/RepeaterHandler.cpp index 13635b3..acd05ea 100644 --- a/RepeaterHandler.cpp +++ b/RepeaterHandler.cpp @@ -59,7 +59,7 @@ bool CRepeaterHandler::m_dtmfEnabled = true; CHeaderLogger* CRepeaterHandler::m_headerLogger = NULL; -CAPRSWriter* CRepeaterHandler::m_aprsWriter = NULL; +CAPRSHandler* CRepeaterHandler::m_aprsWriter = NULL; CCallsignList* CRepeaterHandler::m_restrictList = NULL; @@ -366,7 +366,7 @@ void CRepeaterHandler::setHeaderLogger(CHeaderLogger* logger) m_headerLogger = logger; } -void CRepeaterHandler::setAPRSWriter(CAPRSWriter* writer) +void CRepeaterHandler::setAPRSWriter(CAPRSHandler* writer) { m_aprsWriter = writer; } diff --git a/RepeaterHandler.h b/RepeaterHandler.h index 229b09c..449d34c 100644 --- a/RepeaterHandler.h +++ b/RepeaterHandler.h @@ -48,7 +48,7 @@ #include "CCSHandler.h" #endif #include "StatusData.h" -#include "APRSWriter.h" +#include "APRSHandler.h" #include "HeardData.h" #include "AudioUnit.h" #include "EchoUnit.h" @@ -82,7 +82,7 @@ public: static void setDPlusEnabled(bool enabled); static void setDCSEnabled(bool enabled); static void setHeaderLogger(CHeaderLogger* logger); - static void setAPRSWriter(CAPRSWriter* writer); + static void setAPRSWriter(CAPRSHandler* writer); static void setInfoEnabled(bool enabled); static void setEchoEnabled(bool enabled); static void setDTMFEnabled(bool enabled); @@ -178,7 +178,7 @@ private: static CHeaderLogger* m_headerLogger; - static CAPRSWriter* m_aprsWriter; + static CAPRSHandler* m_aprsWriter; static CCallsignList* m_whiteList; static CCallsignList* m_blackList; From e0ea97d8fc9633c60333e4bbe79947f9e98c5125 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 07:58:11 +0100 Subject: [PATCH 16/59] Rename IRCDDBGatewaysStatusData -> DStarGetwayStatusData --- DStarGatewayStatusData.cpp | 22 +++++++++++----------- DStarGatewayStatusData.h | 6 +++--- DStarGatewayThread.cpp | 8 ++++---- DStarGatewayThread.h | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/DStarGatewayStatusData.cpp b/DStarGatewayStatusData.cpp index 0798788..c3337ee 100644 --- a/DStarGatewayStatusData.cpp +++ b/DStarGatewayStatusData.cpp @@ -21,17 +21,17 @@ #include "DStarGatewayStatusData.h" -CIRCDDBGatewayStatusData::CIRCDDBGatewayStatusData(IRCDDB_STATUS ircDDBStatus, bool dprsStatus) : +CDStarGatewayStatusData::CDStarGatewayStatusData(IRCDDB_STATUS ircDDBStatus, bool dprsStatus) : m_ircDDBStatus(ircDDBStatus), m_dprsStatus(dprsStatus) { } -CIRCDDBGatewayStatusData::~CIRCDDBGatewayStatusData() +CDStarGatewayStatusData::~CDStarGatewayStatusData() { } -void CIRCDDBGatewayStatusData::setRepeater(unsigned int n, const std::string& callsign, LINK_STATUS linkStatus, const std::string& linkCallsign, const std::string& incoming) +void CDStarGatewayStatusData::setRepeater(unsigned int n, const std::string& callsign, LINK_STATUS linkStatus, const std::string& linkCallsign, const std::string& incoming) { assert(n < 4U); @@ -41,50 +41,50 @@ void CIRCDDBGatewayStatusData::setRepeater(unsigned int n, const std::string& ca m_incoming[n] = incoming; } -void CIRCDDBGatewayStatusData::setDongles(const std::string& dongles) +void CDStarGatewayStatusData::setDongles(const std::string& dongles) { m_dongles = dongles; } -IRCDDB_STATUS CIRCDDBGatewayStatusData::getIrcDDBStatus() const +IRCDDB_STATUS CDStarGatewayStatusData::getIrcDDBStatus() const { return m_ircDDBStatus; } -bool CIRCDDBGatewayStatusData::getDPRSStatus() const +bool CDStarGatewayStatusData::getDPRSStatus() const { return m_dprsStatus; } -std::string CIRCDDBGatewayStatusData::getCallsign(unsigned int n) const +std::string CDStarGatewayStatusData::getCallsign(unsigned int n) const { assert(n < 4U); return m_callsign[n]; } -LINK_STATUS CIRCDDBGatewayStatusData::getLinkStatus(unsigned int n) const +LINK_STATUS CDStarGatewayStatusData::getLinkStatus(unsigned int n) const { assert(n < 4U); return m_linkStatus[n]; } -std::string CIRCDDBGatewayStatusData::getLinkCallsign(unsigned int n) const +std::string CDStarGatewayStatusData::getLinkCallsign(unsigned int n) const { assert(n < 4U); return m_linkCallsign[n]; } -std::string CIRCDDBGatewayStatusData::getIncoming(unsigned int n) const +std::string CDStarGatewayStatusData::getIncoming(unsigned int n) const { assert(n < 4U); return m_incoming[n]; } -std::string CIRCDDBGatewayStatusData::getDongles() const +std::string CDStarGatewayStatusData::getDongles() const { return m_dongles; } diff --git a/DStarGatewayStatusData.h b/DStarGatewayStatusData.h index 7768b16..5291508 100644 --- a/DStarGatewayStatusData.h +++ b/DStarGatewayStatusData.h @@ -22,10 +22,10 @@ #include "Defs.h" -class CIRCDDBGatewayStatusData { +class CDStarGatewayStatusData { public: - CIRCDDBGatewayStatusData(IRCDDB_STATUS ircDDBStatus, bool dprsStatus); - ~CIRCDDBGatewayStatusData(); + CDStarGatewayStatusData(IRCDDB_STATUS ircDDBStatus, bool dprsStatus); + ~CDStarGatewayStatusData(); void setRepeater(unsigned int n, const std::string& callsign, LINK_STATUS linkStatus, const std::string& linkCallsign, const std::string& incoming); diff --git a/DStarGatewayThread.cpp b/DStarGatewayThread.cpp index 8c12a6f..b05a151 100644 --- a/DStarGatewayThread.cpp +++ b/DStarGatewayThread.cpp @@ -424,10 +424,10 @@ void* CDStarGatewayThread::Entry() } catch (std::exception& e) { std::string message(e.what()); - CLog::logError("Exception raised in the main thread - \"%s\"", message.c_str()); + CLog::logFatal("Exception raised in the main thread - \"%s\"", message.c_str()); } catch (...) { - CLog::logError("Unknown exception raised in the main thread"); + CLog::logFatal("Unknown exception raised in the main thread"); } CLog::logInfo("Stopping the ircDDB Gateway thread"); @@ -1203,13 +1203,13 @@ void CDStarGatewayThread::writeStatus() file.close(); } -CIRCDDBGatewayStatusData* CDStarGatewayThread::getStatus() const +CDStarGatewayStatusData* CDStarGatewayThread::getStatus() const { bool aprsStatus = false; if (m_aprsWriter != NULL) aprsStatus = m_aprsWriter->isConnected(); - CIRCDDBGatewayStatusData* status = new CIRCDDBGatewayStatusData(m_lastStatus, aprsStatus); + CDStarGatewayStatusData* status = new CDStarGatewayStatusData(m_lastStatus, aprsStatus); for (unsigned int i = 0U; i < 4U; i++) { std::string callsign, linkCallsign; diff --git a/DStarGatewayThread.h b/DStarGatewayThread.h index d90dd26..5203a68 100644 --- a/DStarGatewayThread.h +++ b/DStarGatewayThread.h @@ -83,7 +83,7 @@ virtual void addRepeater(const std::string& callsign, const std::string& band, c virtual void setBlackList(CCallsignList* list); virtual void setRestrictList(CCallsignList* list); - virtual CIRCDDBGatewayStatusData* getStatus() const; + virtual CDStarGatewayStatusData* getStatus() const; virtual void kill(); From 51748fda299ef69fd45add3c3e7e631b922c2c3e Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 07:58:33 +0100 Subject: [PATCH 17/59] #12 move calcCRC to APRSUtils --- APRSUtils.cpp | 23 +++++++++++++++++++++++ APRSUtils.h | 1 + GPSACollector.cpp | 26 ++------------------------ 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/APRSUtils.cpp b/APRSUtils.cpp index 3d0839d..fdb65c2 100644 --- a/APRSUtils.cpp +++ b/APRSUtils.cpp @@ -31,4 +31,27 @@ void CAPRSUtils::dstarCallsignToAPRS(std::string& dstarCallsign) } boost::replace_all(dstarCallsign, " ", "-");//replace remaining blank with a - } +} + +unsigned int CAPRSUtils::calcIcomCRC(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; } \ No newline at end of file diff --git a/APRSUtils.h b/APRSUtils.h index 2ad53d3..517e3d9 100644 --- a/APRSUtils.h +++ b/APRSUtils.h @@ -24,4 +24,5 @@ class CAPRSUtils { public: static void dstarCallsignToAPRS(std::string& dstarCallsign); + static unsigned int calcIcomCRC(const std::string& gpsa); }; \ No newline at end of file diff --git a/GPSACollector.cpp b/GPSACollector.cpp index d6c7b3f..fd539b8 100644 --- a/GPSACollector.cpp +++ b/GPSACollector.cpp @@ -25,6 +25,7 @@ #include "GPSACollector.h" #include "StringUtils.h" #include "Log.h" +#include "APRSUtils.h" const unsigned int APRS_CSUM_LENGTH = 4U; @@ -44,36 +45,13 @@ bool CGPSACollector::isValidGPSA(const std::string& gpsa) if(gpsa.length() < 10U || !boost::starts_with(gpsa, "$$CRC")) return false; - auto csum = calcCRC(gpsa); + auto csum = CAPRSUtils::calcIcomCRC(gpsa); auto csumStr = CStringUtils::string_format("%04X", csum); auto expectedCsum = gpsa.substr(5U, APRS_CSUM_LENGTH); bool res = ::strcasecmp(csumStr.c_str(), expectedCsum.c_str()) == 0; return res; } -unsigned int CGPSACollector::calcCRC(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; -} - unsigned int CGPSACollector::getDataInt(unsigned char * data, unsigned int length) { if(data == nullptr || length == 0U || getSentence().empty()) From a969590099d49b8272fd4ac6a9fd8f97d01cafdd Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 08:19:08 +0100 Subject: [PATCH 18/59] #2 Rename APRSReader -> APRSUnit --- APRSReader.cpp => APRSUnit.cpp | 6 +++--- APRSReader.h => APRSUnit.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename APRSReader.cpp => APRSUnit.cpp (88%) rename APRSReader.h => APRSUnit.h (93%) diff --git a/APRSReader.cpp b/APRSUnit.cpp similarity index 88% rename from APRSReader.cpp rename to APRSUnit.cpp index 431f777..55de1bf 100644 --- a/APRSReader.cpp +++ b/APRSUnit.cpp @@ -17,16 +17,16 @@ */ -#include "APRSReader.h" +#include "APRSUnit.h" #include "APRSFrame.h" #include "APRSParser.h" -CAPRSReader::CAPRSReader() +CAPRSUnit::CAPRSUnit() { } -bool CAPRSReader::readAprsFrame(const std::string& aprsFrame) +bool CAPRSUnit::readAprsFrame(const std::string& aprsFrame) { auto bla = aprsFrame; return false; diff --git a/APRSReader.h b/APRSUnit.h similarity index 93% rename from APRSReader.h rename to APRSUnit.h index 56781ba..dbeb634 100644 --- a/APRSReader.h +++ b/APRSUnit.h @@ -20,10 +20,10 @@ #include "ReadAPRSFrameCallback.h" -class CAPRSReader : public CReadAPRSFrameCallback +class CAPRSUnit : public CReadAPRSFrameCallback { public: - CAPRSReader(); + CAPRSUnit(); bool readAprsFrame(const std::string& aprsFrame); private: From c512e2894a2afde1147edcb0e012d185e6b8e260 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 08:26:38 +0100 Subject: [PATCH 19/59] #12 Rename APRSWriterThread -> APRSHandlerThread --- APRSHandler.cpp | 2 +- APRSHandler.h | 4 +-- APRSWriterThread.cpp => APRSHandlerThread.cpp | 26 +++++++++---------- APRSWriterThread.h => APRSHandlerThread.h | 8 +++--- 4 files changed, 20 insertions(+), 20 deletions(-) rename APRSWriterThread.cpp => APRSHandlerThread.cpp (88%) rename APRSWriterThread.h => APRSHandlerThread.h (79%) diff --git a/APRSHandler.cpp b/APRSHandler.cpp index c36e470..9dcc2b3 100644 --- a/APRSHandler.cpp +++ b/APRSHandler.cpp @@ -47,7 +47,7 @@ m_idFrameProvider(nullptr) assert(!gateway.empty()); assert(!password.empty()); - m_thread = new CAPRSWriterThread(gateway, password, address, hostname, port); + m_thread = new CAPRSHandlerThread(gateway, password, address, hostname, port); m_gateway = gateway; m_gateway = m_gateway.substr(0, LONG_CALLSIGN_LENGTH - 1U); diff --git a/APRSHandler.h b/APRSHandler.h index 9cfa671..bd5ff4e 100644 --- a/APRSHandler.h +++ b/APRSHandler.h @@ -26,7 +26,7 @@ #include "APRSEntry.h" -#include "APRSWriterThread.h" +#include "APRSHandlerThread.h" #include "UDPReaderWriter.h" #include "APRSCollector.h" #include "DStarDefines.h" @@ -59,7 +59,7 @@ public: void close(); private: - CAPRSWriterThread* m_thread; + CAPRSHandlerThread* m_thread; std::string m_gateway; in_addr m_address; unsigned int m_port; diff --git a/APRSWriterThread.cpp b/APRSHandlerThread.cpp similarity index 88% rename from APRSWriterThread.cpp rename to APRSHandlerThread.cpp index 6b39111..aa1438b 100644 --- a/APRSWriterThread.cpp +++ b/APRSHandlerThread.cpp @@ -21,7 +21,7 @@ #include #include -#include "APRSWriterThread.h" +#include "APRSHandlerThread.h" #include "DStarDefines.h" #include "Utils.h" #include "Defs.h" @@ -34,7 +34,7 @@ const unsigned int APRS_TIMEOUT = 10U; -CAPRSWriterThread::CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port) : +CAPRSHandlerThread::CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port) : CThread(), m_username(callsign), m_password(password), @@ -61,7 +61,7 @@ m_clientName(FULL_PRODUCT_NAME) m_ssid = m_ssid.substr(LONG_CALLSIGN_LENGTH - 1U, 1); } -CAPRSWriterThread::CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter, const std::string& clientName) : +CAPRSHandlerThread::CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter, const std::string& clientName) : CThread(), m_username(callsign), m_password(password), @@ -88,7 +88,7 @@ m_clientName(clientName) m_ssid = m_ssid.substr(LONG_CALLSIGN_LENGTH - 1U, 1); } -CAPRSWriterThread::~CAPRSWriterThread() +CAPRSHandlerThread::~CAPRSHandlerThread() { std::vector callBacksCopy; callBacksCopy.assign(m_APRSReadCallbacks.begin(), m_APRSReadCallbacks.end()); @@ -105,7 +105,7 @@ CAPRSWriterThread::~CAPRSWriterThread() m_password.clear(); } -bool CAPRSWriterThread::start() +bool CAPRSHandlerThread::start() { Create(); Run(); @@ -113,7 +113,7 @@ bool CAPRSWriterThread::start() return true; } -void* CAPRSWriterThread::Entry() +void* CAPRSHandlerThread::Entry() { CLog::logInfo("Starting the APRS Writer thread"); @@ -207,13 +207,13 @@ void* CAPRSWriterThread::Entry() return NULL; } -void CAPRSWriterThread::addReadAPRSCallback(CReadAPRSFrameCallback * cb) +void CAPRSHandlerThread::addReadAPRSCallback(CReadAPRSFrameCallback * cb) { assert(cb != nullptr); m_APRSReadCallbacks.push_back(cb); } -void CAPRSWriterThread::write(CAPRSFrame& frame) +void CAPRSHandlerThread::write(CAPRSFrame& frame) { if (!m_connected) return; @@ -228,24 +228,24 @@ void CAPRSWriterThread::write(CAPRSFrame& frame) } } -bool CAPRSWriterThread::isConnected() const +bool CAPRSHandlerThread::isConnected() const { return m_connected; } -void CAPRSWriterThread::stop() +void CAPRSHandlerThread::stop() { m_exit = true; Wait(); } -void CAPRSWriterThread::clock(unsigned int ms) +void CAPRSHandlerThread::clock(unsigned int ms) { m_reconnectTimer.clock(ms); } -bool CAPRSWriterThread::connect() +bool CAPRSHandlerThread::connect() { bool ret = m_socket.open(); if (!ret) @@ -295,7 +295,7 @@ bool CAPRSWriterThread::connect() return true; } -void CAPRSWriterThread::startReconnectionTimer() +void CAPRSHandlerThread::startReconnectionTimer() { // Clamp at a ten minutes reconnect time m_tries++; diff --git a/APRSWriterThread.h b/APRSHandlerThread.h similarity index 79% rename from APRSWriterThread.h rename to APRSHandlerThread.h index e76b20f..a483b41 100644 --- a/APRSWriterThread.h +++ b/APRSHandlerThread.h @@ -29,11 +29,11 @@ #include "APRSFrame.h" -class CAPRSWriterThread : public CThread { +class CAPRSHandlerThread : public CThread { public: - CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port); - CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter, const std::string& clientName); - virtual ~CAPRSWriterThread(); + CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port); + CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter, const std::string& clientName); + virtual ~CAPRSHandlerThread(); bool start(); From 11b9469f45b35004c9f0c9956131185f07e0fd75 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 22:22:36 +0100 Subject: [PATCH 20/59] #12 add destructor --- APRSFrame.cpp | 5 +++++ APRSFrame.h | 1 + 2 files changed, 6 insertions(+) diff --git a/APRSFrame.cpp b/APRSFrame.cpp index 1f8724f..a28ba4c 100644 --- a/APRSFrame.cpp +++ b/APRSFrame.cpp @@ -38,6 +38,11 @@ m_type(type) m_path.assign(path.begin(), path.end()); } +CAPRSFrame::~CAPRSFrame() +{ + m_path.clear(); +} + void CAPRSFrame::clear() { m_source.clear(); diff --git a/APRSFrame.h b/APRSFrame.h index 6c50eee..2fc790c 100644 --- a/APRSFrame.h +++ b/APRSFrame.h @@ -36,6 +36,7 @@ class CAPRSFrame { public: CAPRSFrame(); CAPRSFrame(const std::string& source, const std::string& destination, const std::vector& path, const std::string& body, APRS_FRAME_TYPE type); + ~CAPRSFrame(); void clear(); std::string& getSource() { return m_source; } From 707491d836070d49920714a2a5f8af5b89674ce0 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 22:23:25 +0100 Subject: [PATCH 21/59] #12 add APRSUnit and callbacks --- APRSHandler.cpp | 7 +++- APRSHandler.h | 2 + APRSHandlerThread.cpp | 16 +++---- APRSHandlerThread.h | 6 +-- APRSUnit.cpp | 93 ++++++++++++++++++++++++++++++++++++++--- APRSUnit.h | 28 ++++++++++--- APRSUtils.cpp | 4 +- APRSUtils.h | 2 +- ReadAPRSFrameCallback.h | 7 ++-- RepeaterHandler.cpp | 15 +++++++ RepeaterHandler.h | 9 +++- 11 files changed, 157 insertions(+), 32 deletions(-) diff --git a/APRSHandler.cpp b/APRSHandler.cpp index 9dcc2b3..1ebedd9 100644 --- a/APRSHandler.cpp +++ b/APRSHandler.cpp @@ -47,7 +47,7 @@ m_idFrameProvider(nullptr) assert(!gateway.empty()); assert(!password.empty()); - m_thread = new CAPRSHandlerThread(gateway, password, address, hostname, port); + m_thread = new CAPRSHandlerThread(gateway, password, address, hostname, port, "m/20"); m_gateway = gateway; m_gateway = m_gateway.substr(0, LONG_CALLSIGN_LENGTH - 1U); @@ -244,3 +244,8 @@ void CAPRSHandler::close() m_thread->stop(); } + +void CAPRSHandler::addReadAPRSCallback(IReadAPRSFrameCallback* cb) +{ + m_thread->addReadAPRSCallback(cb); +} diff --git a/APRSHandler.h b/APRSHandler.h index bd5ff4e..743ad37 100644 --- a/APRSHandler.h +++ b/APRSHandler.h @@ -58,6 +58,8 @@ public: void close(); + void addReadAPRSCallback(IReadAPRSFrameCallback* cb); + private: CAPRSHandlerThread* m_thread; std::string m_gateway; diff --git a/APRSHandlerThread.cpp b/APRSHandlerThread.cpp index aa1438b..2c84c65 100644 --- a/APRSHandlerThread.cpp +++ b/APRSHandlerThread.cpp @@ -46,7 +46,7 @@ m_connected(false), m_reconnectTimer(1000U), m_tries(0U), m_APRSReadCallbacks(), -m_filter(""), +m_filter(), m_clientName(FULL_PRODUCT_NAME) { assert(!callsign.empty()); @@ -61,7 +61,7 @@ m_clientName(FULL_PRODUCT_NAME) m_ssid = m_ssid.substr(LONG_CALLSIGN_LENGTH - 1U, 1); } -CAPRSHandlerThread::CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter, const std::string& clientName) : +CAPRSHandlerThread::CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter) : CThread(), m_username(callsign), m_password(password), @@ -74,7 +74,7 @@ m_reconnectTimer(1000U), m_tries(0U), m_APRSReadCallbacks(), m_filter(filter), -m_clientName(clientName) +m_clientName(FULL_PRODUCT_NAME) { assert(!callsign.empty()); assert(!password.empty()); @@ -90,15 +90,11 @@ m_clientName(clientName) CAPRSHandlerThread::~CAPRSHandlerThread() { - std::vector callBacksCopy; + std::vector callBacksCopy; callBacksCopy.assign(m_APRSReadCallbacks.begin(), m_APRSReadCallbacks.end()); m_APRSReadCallbacks.clear(); - for(auto cb : callBacksCopy) { - delete cb; - } - callBacksCopy.clear(); m_username.clear(); @@ -177,7 +173,7 @@ void* CAPRSHandlerThread::Entry() if(CAPRSParser::parseFrame(line, readFrame)) { for(auto cb : m_APRSReadCallbacks) { CAPRSFrame f(readFrame); - cb->readAprsFrame(f); + cb->readAPRSFrame(f); } } } @@ -207,7 +203,7 @@ void* CAPRSHandlerThread::Entry() return NULL; } -void CAPRSHandlerThread::addReadAPRSCallback(CReadAPRSFrameCallback * cb) +void CAPRSHandlerThread::addReadAPRSCallback(IReadAPRSFrameCallback * cb) { assert(cb != nullptr); m_APRSReadCallbacks.push_back(cb); diff --git a/APRSHandlerThread.h b/APRSHandlerThread.h index a483b41..73642ba 100644 --- a/APRSHandlerThread.h +++ b/APRSHandlerThread.h @@ -32,7 +32,7 @@ class CAPRSHandlerThread : public CThread { public: CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port); - CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter, const std::string& clientName); + CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port, const std::string& filter); virtual ~CAPRSHandlerThread(); bool start(); @@ -47,7 +47,7 @@ public: void clock(unsigned int ms); - void addReadAPRSCallback(CReadAPRSFrameCallback* cb); + void addReadAPRSCallback(IReadAPRSFrameCallback* cb); private: std::string m_username; @@ -59,7 +59,7 @@ private: bool m_connected; CTimer m_reconnectTimer; unsigned int m_tries; - std::vector m_APRSReadCallbacks; + std::vector m_APRSReadCallbacks; std::string m_filter; std::string m_clientName; diff --git a/APRSUnit.cpp b/APRSUnit.cpp index 55de1bf..b46a598 100644 --- a/APRSUnit.cpp +++ b/APRSUnit.cpp @@ -16,18 +16,99 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include "APRSUnit.h" -#include "APRSFrame.h" -#include "APRSParser.h" +#include "APRSFormater.h" +#include "StringUtils.h" +#include "APRSUtils.h" +#include "SlowDataEncoder.h" -CAPRSUnit::CAPRSUnit() +CAPRSUnit::CAPRSUnit(IRepeaterCallback * repeaterHandler) : +m_frameBuffer(20U), +m_status(APS_IDLE), +m_repeaterHandler(repeaterHandler), +m_headerData(nullptr), +m_timer(1000U, 2U) { + 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); } -bool CAPRSUnit::readAprsFrame(const std::string& aprsFrame) +void CAPRSUnit::clock(unsigned int ms) { - auto bla = aprsFrame; - return false; + m_timer.clock(ms); + if(m_status == APS_IDLE && !m_frameBuffer.empty() && m_timer.hasExpired()) { + auto frame = m_frameBuffer.front(); + + m_id = CHeaderData::createId(); + + m_headerData = new CHeaderData(); + m_headerData->setMyCall1(frame->getSource()); + m_headerData->setMyCall2("APRS"); + m_headerData->setYourCall("CQCQCQ "); + m_headerData->setId(m_id); + + m_repeaterHandler->process(*m_headerData, DIR_INCOMING, AS_INFO); + + m_status = APS_TRANSMIT; + } + + if(m_status == APS_TRANSMIT && !m_frameBuffer.empty()) + { + auto frame = m_frameBuffer.front(); + std::string frameString; + CAPRSFormater::frameToString(frameString, *frame); + boost::trim_right_if(frameString, [](char c) { return c == '\n' || c == '\r'; }); + frameString.push_back('\r'); + + std::string crc = CStringUtils::string_format("$$CRC%04X", CAPRSUtils::calcGPSAIcomCRC(frameString)); + frameString.insert(0, crc); + + CSlowDataEncoder encoder; + encoder.setHeaderData(*m_headerData); + encoder.setGPSData(frameString); + encoder.setTextData("APRS to DPRS"); + + CAMBEData data; + data.setId(m_id); + + unsigned int out = 0U; + unsigned int dataOut = 0U; + unsigned int needed = (encoder.getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U; + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + + while (dataOut < needed) { + data.setSeq(out); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (out == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + } else { + encoder.getInterleavedData(buffer + VOICE_FRAME_LENGTH_BYTES); + dataOut++; + } + + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + + m_repeaterHandler->process(data, DIR_INCOMING, AS_INFO); + out++; + + if (out == 21U) out = 0U; + } + + m_frameBuffer.pop_front(); + delete frame; + m_status = APS_IDLE; + m_timer.start(); + } } \ No newline at end of file diff --git a/APRSUnit.h b/APRSUnit.h index dbeb634..454c4cd 100644 --- a/APRSUnit.h +++ b/APRSUnit.h @@ -18,16 +18,34 @@ #pragma once -#include "ReadAPRSFrameCallback.h" +#include +#include -class CAPRSUnit : public CReadAPRSFrameCallback +#include "APRSFrame.h" +#include "RepeaterCallback.h" +#include "Timer.h" + +enum APRSUNIT_STATUS { + APS_IDLE, + APS_WAIT, + APS_TRANSMIT +}; + +class CAPRSUnit { public: - CAPRSUnit(); - bool readAprsFrame(const std::string& aprsFrame); + CAPRSUnit(IRepeaterCallback * repeaterHandler); + void writeFrame(CAPRSFrame& aprsFrame); + void clock(unsigned ms); private: - + // CRingBuffer m_frameBuffer; + boost::circular_buffer m_frameBuffer; + APRSUNIT_STATUS m_status; + IRepeaterCallback * m_repeaterHandler; + unsigned int m_id; + CHeaderData * m_headerData; + CTimer m_timer; }; diff --git a/APRSUtils.cpp b/APRSUtils.cpp index fdb65c2..2309344 100644 --- a/APRSUtils.cpp +++ b/APRSUtils.cpp @@ -33,7 +33,7 @@ void CAPRSUtils::dstarCallsignToAPRS(std::string& dstarCallsign) } } -unsigned int CAPRSUtils::calcIcomCRC(const std::string& gpsa) +unsigned int CAPRSUtils::calcGPSAIcomCRC(const std::string& gpsa) { unsigned int icomcrc = 0xFFFFU; @@ -54,4 +54,4 @@ unsigned int CAPRSUtils::calcIcomCRC(const std::string& gpsa) } return ~icomcrc & 0xFFFFU; -} \ No newline at end of file +} diff --git a/APRSUtils.h b/APRSUtils.h index 517e3d9..9455ac9 100644 --- a/APRSUtils.h +++ b/APRSUtils.h @@ -24,5 +24,5 @@ class CAPRSUtils { public: static void dstarCallsignToAPRS(std::string& dstarCallsign); - static unsigned int calcIcomCRC(const std::string& gpsa); + static unsigned int calcGPSAIcomCRC(const std::string& gpsa); }; \ No newline at end of file diff --git a/ReadAPRSFrameCallback.h b/ReadAPRSFrameCallback.h index 9a93193..9ce37d3 100644 --- a/ReadAPRSFrameCallback.h +++ b/ReadAPRSFrameCallback.h @@ -19,12 +19,13 @@ #pragma once #include +#include #include "APRSFrame.h" -class CReadAPRSFrameCallback +class IReadAPRSFrameCallback { public: - virtual ~CReadAPRSFrameCallback(){ } - virtual bool readAprsFrame(CAPRSFrame& aprsFrame) = 0; + virtual void readAPRSFrame(CAPRSFrame& aprsFrame) = 0; }; + diff --git a/RepeaterHandler.cpp b/RepeaterHandler.cpp index acd05ea..e7638d9 100644 --- a/RepeaterHandler.cpp +++ b/RepeaterHandler.cpp @@ -226,6 +226,7 @@ m_heardTimer(1000U, 0U, 100U) // 100ms m_echo = new CEchoUnit(this, callsign); m_infoAudio = new CAudioUnit(this, callsign); m_version = new CVersionUnit(this, callsign); + m_aprsUnit = new CAPRSUnit(this); #ifdef USE_DRATS if (dratsEnabled) { @@ -1416,6 +1417,9 @@ void CRepeaterHandler::clockInt(unsigned int ms) m_echo->clock(ms); m_version->clock(ms); + if(m_aprsUnit != nullptr) + m_aprsUnit->clock(ms); + m_linkReconnectTimer.clock(ms); m_watchdogTimer.clock(ms); m_queryTimer.clock(ms); @@ -2422,6 +2426,10 @@ void CRepeaterHandler::startupInt() m_irc->rptrQTH(callsign, m_latitude, m_longitude, m_description1, m_description2, m_url); } + if(m_aprsWriter != nullptr) { + m_aprsWriter->addReadAPRSCallback(this); + } + #ifdef USE_CCS m_ccsHandler = new CCCSHandler(this, m_rptCallsign, m_index + 1U, m_latitude, m_longitude, m_frequency, m_offset, m_description1, m_description2, m_url, CCS_PORT + m_index); #endif @@ -3069,6 +3077,13 @@ void CRepeaterHandler::triggerInfo() } } +void CRepeaterHandler::readAPRSFrame(CAPRSFrame& frame) +{ + if(m_aprsUnit != nullptr) { + m_aprsUnit->writeFrame(frame); + } +} + #ifdef USE_CCS bool CRepeaterHandler::isCCSCommand(const std::string& command) const { diff --git a/RepeaterHandler.h b/RepeaterHandler.h index 449d34c..678e383 100644 --- a/RepeaterHandler.h +++ b/RepeaterHandler.h @@ -58,11 +58,13 @@ #include "Timer.h" #include "DTMF.h" #include "Defs.h" +#include "ReadAPRSFrameCallback.h" +#include "APRSUnit.h" #include -class CRepeaterHandler : public IRepeaterCallback, public IReflectorCallback, public ICCSCallback { +class CRepeaterHandler : public IRepeaterCallback, public IReflectorCallback, public ICCSCallback, public IReadAPRSFrameCallback { public: static void initialise(unsigned int maxRepeaters); @@ -142,6 +144,8 @@ public: virtual void ccsLinkFailed(const std::string& dtmf, DIRECTION direction); virtual void ccsLinkEnded(const std::string& callsign, DIRECTION direction); + virtual void readAPRSFrame(CAPRSFrame& frame); + protected: #ifdef USE_DRATS CRepeaterHandler(const std::string& callsign, const std::string& band, const std::string& address, unsigned int port, HW_TYPE hwType, const std::string& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const std::string& description1, const std::string& description2, const std::string& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3); @@ -275,6 +279,9 @@ private: // Version information CVersionUnit* m_version; + // APRS to DPRS + CAPRSUnit* m_aprsUnit; + #ifdef USE_DRATS // D-RATS handler CDRATSServer* m_drats; From 30e8be7ea78330e275468e6cd490c0a119577ab7 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 22:24:47 +0100 Subject: [PATCH 22/59] #12 #9 continue RSMS1A reverse engineering --- GPSACollector.cpp | 2 +- RSMS1AMessageBuilder.cpp | 86 +++++++++++++++++++++ RSMS1AMessageBuilder.h | 37 +++++++++ RSMS1AMessageCollector.cpp | 4 + Tests/RSMS1AMessageBuilder/buildMessage.cpp | 61 +++++++++++++++ 5 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 RSMS1AMessageBuilder.cpp create mode 100644 RSMS1AMessageBuilder.h create mode 100644 Tests/RSMS1AMessageBuilder/buildMessage.cpp diff --git a/GPSACollector.cpp b/GPSACollector.cpp index fd539b8..df04ab0 100644 --- a/GPSACollector.cpp +++ b/GPSACollector.cpp @@ -45,7 +45,7 @@ bool CGPSACollector::isValidGPSA(const std::string& gpsa) if(gpsa.length() < 10U || !boost::starts_with(gpsa, "$$CRC")) return false; - auto csum = CAPRSUtils::calcIcomCRC(gpsa); + auto csum = CAPRSUtils::calcGPSAIcomCRC(gpsa); auto csumStr = CStringUtils::string_format("%04X", csum); auto expectedCsum = gpsa.substr(5U, APRS_CSUM_LENGTH); bool res = ::strcasecmp(csumStr.c_str(), expectedCsum.c_str()) == 0; diff --git a/RSMS1AMessageBuilder.cpp b/RSMS1AMessageBuilder.cpp new file mode 100644 index 0000000..53a91ef --- /dev/null +++ b/RSMS1AMessageBuilder.cpp @@ -0,0 +1,86 @@ +/* + * 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 "RSMS1AMessageBuilder.h" +#include "StringUtils.h" + +std::vector CRSMS1AMessageBuilder::m_charsToEscape = {-17, 0, 17, 19, -2, -25, 26, -3, -1, 36, 13, 44}; + +void CRSMS1AMessageBuilder::buildMessage(std::string& message, const std::string& sender, const std::string& recipient, const std::string body) +{ + + std::string bodyTmp(body); + auto bodyCrc = calculateBodyCRC(body); + bodyTmp.push_back(bodyCrc); + escapeBody(bodyTmp, std::string(bodyTmp)); + + std::string header = CStringUtils::string_format("%s,%s,0011", sender.c_str(), recipient.c_str()); + char c1, c2; + calcMsgIcomCRC(header, c1, c2); + header.push_back(c1); + header.push_back(c2); + message = "$$Msg," + header + bodyTmp + '\n'; +} + +char CRSMS1AMessageBuilder::calculateBodyCRC(const std::string& body) +{ + if(body.length() == 1) + return body[0]; + + int num = 0; + for(auto c : body) { + num += c; + } + + return (char)((num & 255) /*- 128*/); +} + +void CRSMS1AMessageBuilder::escapeBody(std::string output, const std::string& body) +{ + output.clear(); + for(char c : body) { + if(std::find(m_charsToEscape.begin(), m_charsToEscape.end(), c) != m_charsToEscape.end()) { + output.push_back(-17); + } + output.push_back(c); + } +} + +void CRSMS1AMessageBuilder::calcMsgIcomCRC(const std::string& msg, char& c1, char& c2) +{ + int num = 0; + for(unsigned int i = 0U; i < msg.length(); i++) { + num += msg[i]; + } + + c1 = doWhatever((char)((num >> 4) & 15)); + c2 = doWhatever((char)(num & 15)); +} + +char CRSMS1AMessageBuilder::doWhatever(char b2) { + int i; + int i2 = b2 & 255; + if (i2 >= 0 && i2 <= 9) { + i = b2 + 48; + } else if (10 > i2 || i2 > 15) { + return 0; + } else { + i = b2 + 55; + } + return (char) i; +} \ No newline at end of file diff --git a/RSMS1AMessageBuilder.h b/RSMS1AMessageBuilder.h new file mode 100644 index 0000000..d73f748 --- /dev/null +++ b/RSMS1AMessageBuilder.h @@ -0,0 +1,37 @@ +/* + * 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 +#include + +class CRSMS1AMessageBuilder +{ +public: + static void buildMessage(std::string& message, const std::string& sender, const std::string& recipient, const std::string body); + +private: + static void calcMsgIcomCRC(const std::string& msg, char& c1, char& c2); + static void escapeBody(std::string output, const std::string& body); + static void escapeBytes(std::vector output, const std::vector input); + static char calculateBodyCRC(const std::string& body); + static char doWhatever(char b2); + + static std::vector m_charsToEscape; +}; \ No newline at end of file diff --git a/RSMS1AMessageCollector.cpp b/RSMS1AMessageCollector.cpp index f9cafe6..bb30923 100644 --- a/RSMS1AMessageCollector.cpp +++ b/RSMS1AMessageCollector.cpp @@ -57,6 +57,10 @@ bool CRSMS1AMessageCollector::isValidMsg(const std::string& msg) bool ret = splits.size() >= 4 && !splits[1].empty() && !splits[2].empty(); + + CUtils::dump("RS-MS1A:", (unsigned char *)msg.c_str(), msg.length() + 1U); + CLog::logDebug("RS-MS1A: %s", msg.c_str()); + return ret; //TODO 2022-01-01 figure out what the heck it is about thic strange CRCs diff --git a/Tests/RSMS1AMessageBuilder/buildMessage.cpp b/Tests/RSMS1AMessageBuilder/buildMessage.cpp new file mode 100644 index 0000000..8ed29ab --- /dev/null +++ b/Tests/RSMS1AMessageBuilder/buildMessage.cpp @@ -0,0 +1,61 @@ +/* + * 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 + +#include "../../RSMS1AMessageBuilder.h" + +namespace RSMS1AMessageBuilder +{ + + class RSMS1AMessageBuilder_buildMessage : public ::testing::Test { + + }; + + TEST_F(RSMS1AMessageBuilder_buildMessage, testABC) + { + std::string message; + CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "ABC"); + + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118ABCF\n"); + } + + TEST_F(RSMS1AMessageBuilder_buildMessage, testA) + { + std::string message; + CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "A"); + + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\n"); + } + + TEST_F(RSMS1AMessageBuilder_buildMessage, testAA) + { + std::string message; + CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "AA"); + + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\02\n"); + } + + TEST_F(RSMS1AMessageBuilder_buildMessage, testSalutCommentVasTu) + { + std::string message; + CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "Salut, comment vas tu?"); + + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?\x7A\n"); + } +}; \ No newline at end of file From 8b7fbd22d370bd6ebd1b91afcbfbc788ea7c9d69 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 22:25:21 +0100 Subject: [PATCH 23/59] file assoc --- .vscode/settings.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a509b7e..a75b395 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,16 @@ } ], "files.associations": { - "new": "cpp" + "new": "cpp", + "type_traits": "cpp", + "array": "cpp", + "*.tcc": "cpp", + "chrono": "cpp", + "deque": "cpp", + "list": "cpp", + "vector": "cpp", + "optional": "cpp", + "stop_token": "cpp" }, "editor.tokenColorCustomizations": { "textMateRules": [ From 6cc1a50c79d5398d0f863dbcafd67de6e2319a89 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 6 Jan 2022 22:25:34 +0100 Subject: [PATCH 24/59] change default task --- .vscode/tasks.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2dc1e81..ab94961 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -11,10 +11,7 @@ "-j3", "USE_GPSD=1" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] }, { @@ -26,7 +23,10 @@ "tests", "USE_GPSD=1" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] } ] From a9953aec29eea075f72e3bb40358f238c555458d Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 7 Jan 2022 06:38:46 +0100 Subject: [PATCH 25/59] #9 #12 fix escaping and checksum calculation --- RSMS1AMessageBuilder.cpp | 12 ++++------ RSMS1AMessageBuilder.h | 2 +- Tests/RSMS1AMessageBuilder/buildMessage.cpp | 26 +++++++++++++++++---- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/RSMS1AMessageBuilder.cpp b/RSMS1AMessageBuilder.cpp index 53a91ef..2351d97 100644 --- a/RSMS1AMessageBuilder.cpp +++ b/RSMS1AMessageBuilder.cpp @@ -23,11 +23,9 @@ std::vector CRSMS1AMessageBuilder::m_charsToEscape = {-17, 0, 17, 19, -2, void CRSMS1AMessageBuilder::buildMessage(std::string& message, const std::string& sender, const std::string& recipient, const std::string body) { - - std::string bodyTmp(body); auto bodyCrc = calculateBodyCRC(body); - bodyTmp.push_back(bodyCrc); - escapeBody(bodyTmp, std::string(bodyTmp)); + std::string bodyTmp; + escapeBody(bodyTmp, body + bodyCrc); std::string header = CStringUtils::string_format("%s,%s,0011", sender.c_str(), recipient.c_str()); char c1, c2; @@ -47,15 +45,15 @@ char CRSMS1AMessageBuilder::calculateBodyCRC(const std::string& body) num += c; } - return (char)((num & 255) /*- 128*/); + return (char)((num & 255) - 128); } -void CRSMS1AMessageBuilder::escapeBody(std::string output, const std::string& body) +void CRSMS1AMessageBuilder::escapeBody(std::string& output, const std::string& body) { output.clear(); for(char c : body) { if(std::find(m_charsToEscape.begin(), m_charsToEscape.end(), c) != m_charsToEscape.end()) { - output.push_back(-17); + output.push_back('o'); } output.push_back(c); } diff --git a/RSMS1AMessageBuilder.h b/RSMS1AMessageBuilder.h index d73f748..4819fde 100644 --- a/RSMS1AMessageBuilder.h +++ b/RSMS1AMessageBuilder.h @@ -28,7 +28,7 @@ public: private: static void calcMsgIcomCRC(const std::string& msg, char& c1, char& c2); - static void escapeBody(std::string output, const std::string& body); + static void escapeBody(std::string& output, const std::string& body); static void escapeBytes(std::vector output, const std::vector input); static char calculateBodyCRC(const std::string& body); static char doWhatever(char b2); diff --git a/Tests/RSMS1AMessageBuilder/buildMessage.cpp b/Tests/RSMS1AMessageBuilder/buildMessage.cpp index 8ed29ab..17b2550 100644 --- a/Tests/RSMS1AMessageBuilder/buildMessage.cpp +++ b/Tests/RSMS1AMessageBuilder/buildMessage.cpp @@ -27,7 +27,7 @@ namespace RSMS1AMessageBuilder }; - TEST_F(RSMS1AMessageBuilder_buildMessage, testABC) + TEST_F(RSMS1AMessageBuilder_buildMessage, ABC) { std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "ABC"); @@ -35,7 +35,7 @@ namespace RSMS1AMessageBuilder EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118ABCF\n"); } - TEST_F(RSMS1AMessageBuilder_buildMessage, testA) + TEST_F(RSMS1AMessageBuilder_buildMessage, A) { std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "A"); @@ -43,7 +43,7 @@ namespace RSMS1AMessageBuilder EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\n"); } - TEST_F(RSMS1AMessageBuilder_buildMessage, testAA) + TEST_F(RSMS1AMessageBuilder_buildMessage, AA) { std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "AA"); @@ -51,11 +51,27 @@ namespace RSMS1AMessageBuilder EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\02\n"); } - TEST_F(RSMS1AMessageBuilder_buildMessage, testSalutCommentVasTu) + TEST_F(RSMS1AMessageBuilder_buildMessage, SalutCommentVasTu) { std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "Salut, comment vas tu?"); - EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?\x7A\n"); + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\n"); + } + + TEST_F(RSMS1AMessageBuilder_buildMessage, escapeComma) + { + std::string message; + CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", ","); + + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118o,o,\n"); + } + + TEST_F(RSMS1AMessageBuilder_buildMessage, INeedMoreDollars) + { + std::string message; + CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "I need more $$$$"); + + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118I need more o$o$o$o$\x08\n"); } }; \ No newline at end of file From bfd95ad426902e714ccc1180d47c85a4b3607907 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 7 Jan 2022 13:21:40 +0100 Subject: [PATCH 26/59] #9 only dumpif ok --- RSMS1AMessageCollector.cpp | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/RSMS1AMessageCollector.cpp b/RSMS1AMessageCollector.cpp index bb30923..d0a2ade 100644 --- a/RSMS1AMessageCollector.cpp +++ b/RSMS1AMessageCollector.cpp @@ -58,8 +58,12 @@ bool CRSMS1AMessageCollector::isValidMsg(const std::string& msg) && !splits[1].empty() && !splits[2].empty(); - CUtils::dump("RS-MS1A:", (unsigned char *)msg.c_str(), msg.length() + 1U); - CLog::logDebug("RS-MS1A: %s", msg.c_str()); + if(ret) { + CUtils::dump("RS-MS1A:", (unsigned char *)msg.c_str(), msg.length() + 1U); + CLog::logDebug("RS-MS1A: %s", msg.c_str()); + + + } return ret; @@ -78,32 +82,6 @@ bool CRSMS1AMessageCollector::isValidMsg(const std::string& msg) // return res; } -unsigned int CRSMS1AMessageCollector::calcCRC(const std::string& gpsa, unsigned int start, unsigned int length) -{ - unsigned int icomcrc = 0xFFFFU; - auto end = start + length; - if(end > gpsa.length()) { - end = gpsa.length(); - } - - for (unsigned int j = start; j < end; 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; -} - unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned int length) { if(data == nullptr || length == 0U || getSentence().empty()) From 18bb1f365b7cf2fd867a698b2488dabf34f9d00f Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 7 Jan 2022 21:23:21 +0100 Subject: [PATCH 27/59] #9 add message crc checks, fix check calculation --- RSMS1AMessageBuilder.cpp | 81 ++++++++++++++++++++- RSMS1AMessageBuilder.h | 8 ++ RSMS1AMessageCollector.cpp | 74 +++---------------- Tests/RSMS1AMessageBuilder/buildMessage.cpp | 21 ++++-- Tests/RSMS1AMessageBuilder/parseMessage.cpp | 67 +++++++++++++++++ 5 files changed, 178 insertions(+), 73 deletions(-) create mode 100644 Tests/RSMS1AMessageBuilder/parseMessage.cpp diff --git a/RSMS1AMessageBuilder.cpp b/RSMS1AMessageBuilder.cpp index 2351d97..8d46f9b 100644 --- a/RSMS1AMessageBuilder.cpp +++ b/RSMS1AMessageBuilder.cpp @@ -16,6 +16,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + #include "RSMS1AMessageBuilder.h" #include "StringUtils.h" @@ -25,14 +27,14 @@ void CRSMS1AMessageBuilder::buildMessage(std::string& message, const std::string { auto bodyCrc = calculateBodyCRC(body); std::string bodyTmp; - escapeBody(bodyTmp, body + bodyCrc); + escapeBody(bodyTmp, body + (char)bodyCrc); std::string header = CStringUtils::string_format("%s,%s,0011", sender.c_str(), recipient.c_str()); char c1, c2; calcMsgIcomCRC(header, c1, c2); header.push_back(c1); header.push_back(c2); - message = "$$Msg," + header + bodyTmp + '\n'; + message = "$$Msg," + header + bodyTmp + '\r'; } char CRSMS1AMessageBuilder::calculateBodyCRC(const std::string& body) @@ -40,12 +42,16 @@ char CRSMS1AMessageBuilder::calculateBodyCRC(const std::string& body) if(body.length() == 1) return body[0]; - int num = 0; + unsigned int num = 0; for(auto c : body) { num += c; } - return (char)((num & 255) - 128); + auto res = (num & 255); + if(res >= 128) + res -= 128; + + return (char)res; } void CRSMS1AMessageBuilder::escapeBody(std::string& output, const std::string& body) @@ -81,4 +87,71 @@ char CRSMS1AMessageBuilder::doWhatever(char b2) { i = b2 + 55; } return (char) i; +} + +RSMS1A_PARSE_STATUS CRSMS1AMessageBuilder::parseMessage(std::string& sender, std::string& recipient, std::string& body, const std::string& message) +{ + sender.clear(); + recipient.clear(); + body.clear(); + + if(!boost::starts_with(message, "$$Msg,")) + return RSMS_FAIL; + + auto firstCommaPos = message.find_first_of(','); + if(firstCommaPos == std::string::npos || (firstCommaPos + 1) >= message.length()) + return RSMS_FAIL; + + auto secondCommaPos = message.find_first_of(',', firstCommaPos + 1); + if(secondCommaPos == std::string::npos || (secondCommaPos + 1) >= message.length()) + return RSMS_FAIL; + + auto thirdCommaPos = message.find_first_of(',', secondCommaPos + 1); + if(thirdCommaPos == std::string::npos || (thirdCommaPos + 1 + 6) >= message.length()) + return RSMS_FAIL; + + sender.assign(message.substr(firstCommaPos + 1, secondCommaPos - firstCommaPos - 1)); + recipient.assign(message.substr(secondCommaPos + 1, thirdCommaPos - secondCommaPos - 1)); + body.assign(message.substr(thirdCommaPos + 1 + 6)); + unescapeBody(body, std::string(body)); + if(body.length() >= 2U) body.resize(body.length() - 2U); + + // build a message out of what we received in order to check if all checksums matches + // use only header of message to match checksum + std::string messagecheck; + buildMessage(messagecheck, sender, recipient, " "); + messagecheck.resize(messagecheck.length() - 3U);// get rid of body, body check byte and trailing \r + + if(messagecheck != message.substr(0, messagecheck.length())) { + sender.clear(); + recipient.clear(); + body.clear(); + return RSMS_FAIL; // we do not allow any errors in message header + } + + //rebuild complete messsage with body + buildMessage(messagecheck, sender, recipient, body); + if(messagecheck != message) + return RSMS_ERRONEOUS_MSG; // we allow erros to occur in the message body + + return RSMS_OK; +} + +void CRSMS1AMessageBuilder::unescapeBody(std::string& output, const std::string& body) +{ + output.clear(); + if(body.empty()) + return; + + for(unsigned int i = 0U; i < body.length(); i++) { + auto c = body[i]; + auto next = body[i + 1U]; + if(c == 'o' && std::find(m_charsToEscape.begin(), m_charsToEscape.end(), next) != m_charsToEscape.end()) { + output.push_back(next); + i++; + continue; + } + + output.push_back(c); + } } \ No newline at end of file diff --git a/RSMS1AMessageBuilder.h b/RSMS1AMessageBuilder.h index 4819fde..2b13d4b 100644 --- a/RSMS1AMessageBuilder.h +++ b/RSMS1AMessageBuilder.h @@ -21,14 +21,22 @@ #include #include +enum RSMS1A_PARSE_STATUS { + RSMS_FAIL, + RSMS_ERRONEOUS_MSG, + RSMS_OK +}; + class CRSMS1AMessageBuilder { public: static void buildMessage(std::string& message, const std::string& sender, const std::string& recipient, const std::string body); + static RSMS1A_PARSE_STATUS parseMessage(std::string& sender, std::string& recipient, std::string& body, const std::string& message); private: static void calcMsgIcomCRC(const std::string& msg, char& c1, char& c2); static void escapeBody(std::string& output, const std::string& body); + static void unescapeBody(std::string& output, const std::string& body); static void escapeBytes(std::vector output, const std::vector input); static char calculateBodyCRC(const std::string& body); static char doWhatever(char b2); diff --git a/RSMS1AMessageCollector.cpp b/RSMS1AMessageCollector.cpp index d0a2ade..fe91dfc 100644 --- a/RSMS1AMessageCollector.cpp +++ b/RSMS1AMessageCollector.cpp @@ -30,6 +30,7 @@ #include "Log.h" #include "Utils.h" #include "APRSUtils.h" +#include "RSMS1AMessageBuilder.h" const unsigned int APRS_CSUM_LENGTH = 4U; @@ -47,39 +48,8 @@ bool CRSMS1AMessageCollector::isValidSentence(const std::string& sentence) bool CRSMS1AMessageCollector::isValidMsg(const std::string& msg) { - if(msg.empty() || !boost::starts_with(msg, "$$Msg")) - return false; - - // CUtils::dump("RS-MS1A:", (unsigned char *)msg.c_str(), msg.length()); - std::vector splits; - boost::split(splits, msg, boost::is_any_of(",")); - - bool ret = splits.size() >= 4 - && !splits[1].empty() - && !splits[2].empty(); - - if(ret) { - CUtils::dump("RS-MS1A:", (unsigned char *)msg.c_str(), msg.length() + 1U); - CLog::logDebug("RS-MS1A: %s", msg.c_str()); - - - } - - return ret; - - //TODO 2022-01-01 figure out what the heck it is about thic strange CRCs - - // CUtils::dump("RS-MS1A:", (unsigned char *)gpsa.c_str(), gpsa.length() + 1U); - // CLog::logDebug("RS-MS1A: %s", gpsa.c_str()); - - // auto thirdCommaPos = CStringUtils::find_nth(gpsa, 0U, ',', 3); - // auto csum = calcCRC(gpsa, thirdCommaPos + 6 + 1, gpsa.length() - thirdCommaPos - 2U - 6U); - // auto csumStr = CStringUtils::string_format("%06X", csum); - // CLog::logDebug("RS-MS1A CRC: %s", csumStr.c_str()); - - // auto expectedCsum = gpsa.substr(5U, APRS_CSUM_LENGTH); - // bool res = ::strcasecmp(csumStr.c_str(), expectedCsum.c_str()) == 0; - // return res; + // checking validity involves parsing, so we do minumum checks here. Big chekc done in getDataInt + return !msg.empty() && boost::starts_with(msg, "$$Msg"); } unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned int length) @@ -87,40 +57,18 @@ unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned if(data == nullptr || length == 0U || getSentence().empty()) return 0U; - auto sentence = getSentence(); - - std::vector splits; - boost::split(splits, sentence, boost::is_any_of(",")); + std::string sender, recipient, body, sentence; + sentence = getSentence(); + auto parseRes = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, sentence); + + CUtils::dump(CStringUtils::string_format("RS-MS1A Message parsed with result %s", parseRes == RSMS_OK ? "OK" : (parseRes == RSMS_ERRONEOUS_MSG ? "Error in msg body" : "failed")).c_str(), + (unsigned char *)sentence.c_str(), sentence.length()); - bool ret = splits.size() >= 4 - && !splits[1].empty() - && !splits[2].empty(); - if(!ret) { + if(parseRes == RSMS_FAIL) return 0U; - } - - auto sender = splits[1]; - auto recipient = CUtils::ToUpper(splits[2]); - - for(unsigned int i = 0;i < 3; i++) { - splits.erase(splits.begin()); - } - - auto message = boost::join(splits, ","); - if(message.length() > 6) - message = message.substr(6); auto seqNum = rand() % 0xFFFFFU; - - CAPRSUtils::dstarCallsignToAPRS(sender); - CAPRSUtils::dstarCallsignToAPRS(recipient); - recipient.resize(9, ' '); - message.resize(std::max(0 , ((int)message.length()) - 2)); - - //unescape commas in message body - boost::replace_all(message, "o,", ","); - - auto aprsFrame = CStringUtils::string_format("%s-5>APDPRS,DSTAR*::%s:%s{%05X\r\n", sender.c_str(), recipient.c_str(), message.c_str(), seqNum); + auto aprsFrame = CStringUtils::string_format("%s-5>APDPRS,DSTAR*::%s:%s{%05X", sender.c_str(), recipient.c_str(), body.c_str(), seqNum); auto aprsFrameLen = aprsFrame.length(); diff --git a/Tests/RSMS1AMessageBuilder/buildMessage.cpp b/Tests/RSMS1AMessageBuilder/buildMessage.cpp index 17b2550..6312e71 100644 --- a/Tests/RSMS1AMessageBuilder/buildMessage.cpp +++ b/Tests/RSMS1AMessageBuilder/buildMessage.cpp @@ -32,7 +32,7 @@ namespace RSMS1AMessageBuilder std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "ABC"); - EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118ABCF\n"); + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118ABCF\r"); } TEST_F(RSMS1AMessageBuilder_buildMessage, A) @@ -40,7 +40,7 @@ namespace RSMS1AMessageBuilder std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "A"); - EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\n"); + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\r"); } TEST_F(RSMS1AMessageBuilder_buildMessage, AA) @@ -48,7 +48,7 @@ namespace RSMS1AMessageBuilder std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "AA"); - EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\02\n"); + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\02\r"); } TEST_F(RSMS1AMessageBuilder_buildMessage, SalutCommentVasTu) @@ -56,7 +56,7 @@ namespace RSMS1AMessageBuilder std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "Salut, comment vas tu?"); - EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\n"); + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\r"); } TEST_F(RSMS1AMessageBuilder_buildMessage, escapeComma) @@ -64,7 +64,7 @@ namespace RSMS1AMessageBuilder std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", ","); - EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118o,o,\n"); + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118o,o,\r"); } TEST_F(RSMS1AMessageBuilder_buildMessage, INeedMoreDollars) @@ -72,6 +72,15 @@ namespace RSMS1AMessageBuilder std::string message; CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "I need more $$$$"); - EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118I need more o$o$o$o$\x08\n"); + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118I need more o$o$o$o$\x08\r"); + } + + //"$$Msg,KC3FRA,F4FXL 7,00116Fhello\024\r" + TEST_F(RSMS1AMessageBuilder_buildMessage, hello) + { + std::string message; + CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL 7", "hello"); + + EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL 7,00116Fhello\024\r"); } }; \ No newline at end of file diff --git a/Tests/RSMS1AMessageBuilder/parseMessage.cpp b/Tests/RSMS1AMessageBuilder/parseMessage.cpp new file mode 100644 index 0000000..ea155ac --- /dev/null +++ b/Tests/RSMS1AMessageBuilder/parseMessage.cpp @@ -0,0 +1,67 @@ +/* + * 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 + +#include "../../RSMS1AMessageBuilder.h" + +namespace RSMS1AMessageBuilder +{ + class RSMS1AMessageBuilder_parseMessage : public ::testing::Test { + + }; + + TEST_F(RSMS1AMessageBuilder_parseMessage, NoError) + { + std::string message = "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\r"; + std::string sender, recipient, body; + + auto ret = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, message); + + EXPECT_EQ(ret, RSMS_OK); + EXPECT_STREQ(sender.c_str(), "KC3FRA"); + EXPECT_STREQ(recipient.c_str(), "F4FXL"); + EXPECT_STREQ(body.c_str(), "Salut, comment vas tu?"); + } + + TEST_F(RSMS1AMessageBuilder_parseMessage, ErrorInMessage) + { + std::string message = "$$Msg,KC3FRA,F4FXL,001118Saluto, comlent vas tu?z\r"; + std::string sender, recipient, body; + + auto ret = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, message); + + EXPECT_EQ(ret, RSMS_ERRONEOUS_MSG); + EXPECT_STREQ(sender.c_str(), "KC3FRA"); + EXPECT_STREQ(recipient.c_str(), "F4FXL"); + EXPECT_STREQ(body.c_str(), "Salut, comlent vas tu?"); + } + + TEST_F(RSMS1AMessageBuilder_parseMessage, ErrorInHeader) + { + std::string message = "$$Msg,KC3FRB,F4FXL,001118Saluto, comment vas tu?z\r"; + std::string sender, recipient, body; + + auto ret = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, message); + + EXPECT_EQ(ret, RSMS_FAIL); + EXPECT_STREQ(sender.c_str(), ""); + EXPECT_STREQ(recipient.c_str(), ""); + EXPECT_STREQ(body.c_str(), ""); + } +}; \ No newline at end of file From a54eb90b273025789564032a73c9de060f4ae65d Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 7 Jan 2022 22:58:07 +0100 Subject: [PATCH 28/59] #9 make every sign explicit --- RSMS1AMessageBuilder.cpp | 24 ++++++++++++------------ RSMS1AMessageBuilder.h | 8 ++++---- Tests/Utils/swap_endian_be.cpp | 13 ++++++++++++- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/RSMS1AMessageBuilder.cpp b/RSMS1AMessageBuilder.cpp index 8d46f9b..e7d3d34 100644 --- a/RSMS1AMessageBuilder.cpp +++ b/RSMS1AMessageBuilder.cpp @@ -21,7 +21,7 @@ #include "RSMS1AMessageBuilder.h" #include "StringUtils.h" -std::vector CRSMS1AMessageBuilder::m_charsToEscape = {-17, 0, 17, 19, -2, -25, 26, -3, -1, 36, 13, 44}; +std::vector CRSMS1AMessageBuilder::m_charsToEscape = {-17, 0, 17, 19, -2, -25, 26, -3, -1, 36, 13, 44}; void CRSMS1AMessageBuilder::buildMessage(std::string& message, const std::string& sender, const std::string& recipient, const std::string body) { @@ -30,28 +30,28 @@ void CRSMS1AMessageBuilder::buildMessage(std::string& message, const std::string escapeBody(bodyTmp, body + (char)bodyCrc); std::string header = CStringUtils::string_format("%s,%s,0011", sender.c_str(), recipient.c_str()); - char c1, c2; + signed char c1, c2; calcMsgIcomCRC(header, c1, c2); header.push_back(c1); header.push_back(c2); message = "$$Msg," + header + bodyTmp + '\r'; } -char CRSMS1AMessageBuilder::calculateBodyCRC(const std::string& body) +signed char CRSMS1AMessageBuilder::calculateBodyCRC(const std::string& body) { if(body.length() == 1) return body[0]; - unsigned int num = 0; - for(auto c : body) { + signed int num = 0; + for(signed char c : body) { num += c; } - auto res = (num & 255); + signed int res = (num & 255); if(res >= 128) res -= 128; - return (char)res; + return (signed char)res; } void CRSMS1AMessageBuilder::escapeBody(std::string& output, const std::string& body) @@ -65,18 +65,18 @@ void CRSMS1AMessageBuilder::escapeBody(std::string& output, const std::string& b } } -void CRSMS1AMessageBuilder::calcMsgIcomCRC(const std::string& msg, char& c1, char& c2) +void CRSMS1AMessageBuilder::calcMsgIcomCRC(const std::string& msg, signed char& c1, signed char& c2) { int num = 0; for(unsigned int i = 0U; i < msg.length(); i++) { num += msg[i]; } - c1 = doWhatever((char)((num >> 4) & 15)); - c2 = doWhatever((char)(num & 15)); + c1 = doWhatever((signed char)((num >> 4) & 15)); + c2 = doWhatever((signed char)(num & 15)); } -char CRSMS1AMessageBuilder::doWhatever(char b2) { +signed char CRSMS1AMessageBuilder::doWhatever(signed char b2) { int i; int i2 = b2 & 255; if (i2 >= 0 && i2 <= 9) { @@ -86,7 +86,7 @@ char CRSMS1AMessageBuilder::doWhatever(char b2) { } else { i = b2 + 55; } - return (char) i; + return (signed char) i; } RSMS1A_PARSE_STATUS CRSMS1AMessageBuilder::parseMessage(std::string& sender, std::string& recipient, std::string& body, const std::string& message) diff --git a/RSMS1AMessageBuilder.h b/RSMS1AMessageBuilder.h index 2b13d4b..142c0e2 100644 --- a/RSMS1AMessageBuilder.h +++ b/RSMS1AMessageBuilder.h @@ -34,12 +34,12 @@ public: static RSMS1A_PARSE_STATUS parseMessage(std::string& sender, std::string& recipient, std::string& body, const std::string& message); private: - static void calcMsgIcomCRC(const std::string& msg, char& c1, char& c2); + static void calcMsgIcomCRC(const std::string& msg, signed char& c1, signed char& c2); static void escapeBody(std::string& output, const std::string& body); static void unescapeBody(std::string& output, const std::string& body); static void escapeBytes(std::vector output, const std::vector input); - static char calculateBodyCRC(const std::string& body); - static char doWhatever(char b2); + static signed char calculateBodyCRC(const std::string& body); + static signed char doWhatever(signed char b2); - static std::vector m_charsToEscape; + static std::vector m_charsToEscape; }; \ No newline at end of file diff --git a/Tests/Utils/swap_endian_be.cpp b/Tests/Utils/swap_endian_be.cpp index 7099a0e..8e3a817 100644 --- a/Tests/Utils/swap_endian_be.cpp +++ b/Tests/Utils/swap_endian_be.cpp @@ -19,6 +19,8 @@ #include #include "../../Utils.h" +#include "../../StringUtils.h" +#include "../../Log.h" class Utils_swap_endian_be : public ::testing::Test { @@ -31,4 +33,13 @@ TEST_F(Utils_swap_endian_be, SwapUINT32_be) { uint32_t res = CUtils::swap_endian_be(test); EXPECT_EQ(res, 0x12345678U); -} \ No newline at end of file +} + + +TEST_F(Utils_swap_endian_be, blabla) { + std::vector charsToEscape = {-17, 0, 17, 19, 128-2, -25, 26, 128-3, -1, 36, 13, 44}; + + for(unsigned int i = 1U; i < charsToEscape.size(); i++) { + CLog::logDebug("%08x - %08x", charsToEscape[i], (unsigned char)charsToEscape[i]); + } +} From 6bd5b2dfc6472446a80bfd52b66384dccf8a0de4 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 7 Jan 2022 23:15:42 +0100 Subject: [PATCH 29/59] #9 correctly format source and dest callsigns --- RSMS1AMessageCollector.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RSMS1AMessageCollector.cpp b/RSMS1AMessageCollector.cpp index fe91dfc..03c36dd 100644 --- a/RSMS1AMessageCollector.cpp +++ b/RSMS1AMessageCollector.cpp @@ -67,6 +67,10 @@ unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned if(parseRes == RSMS_FAIL) return 0U; + CAPRSUtils::dstarCallsignToAPRS(sender); + CAPRSUtils::dstarCallsignToAPRS(recipient); + recipient.resize(9U, ' '); + auto seqNum = rand() % 0xFFFFFU; auto aprsFrame = CStringUtils::string_format("%s-5>APDPRS,DSTAR*::%s:%s{%05X", sender.c_str(), recipient.c_str(), body.c_str(), seqNum); From 9497bf9ba0691d06db31a233df44dd4785bcb81a Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 7 Jan 2022 23:26:25 +0100 Subject: [PATCH 30/59] #9 exit if did not collect any data --- APRSHandler.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/APRSHandler.cpp b/APRSHandler.cpp index 1ebedd9..c8539fc 100644 --- a/APRSHandler.cpp +++ b/APRSHandler.cpp @@ -130,6 +130,11 @@ void CAPRSHandler::writeData(const std::string& callsign, const CAMBEData& data) } unsigned int length = collector->getData(SLOW_DATA_TYPE_GPS, buffer, 400U); + + if(length == 0U) { + return; + } + std::string text((char*)buffer, length); CAPRSFrame frame; From f17518d7238041f7357b7d7b2ffbcfd5edd54dc9 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 8 Jan 2022 18:22:47 +0100 Subject: [PATCH 31/59] remove tests from default build task --- .vscode/tasks.json | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 37a88a4..168ca95 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,11 +9,13 @@ "command": "make", "args": [ "-j3", - "tests", "ENABLE_DEBUG=1", - "USE_GPSD=1", + "USE_GPSD=1" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] }, { @@ -24,12 +26,9 @@ "-j3", "tests", "ENABLE_DEBUG=1", - "USE_GPSD=1", + "USE_GPSD=1" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] } ] From f18452ddfa04a2a774706e5c4f4fc2a17f3fa7b1 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 8 Jan 2022 19:45:46 +0100 Subject: [PATCH 32/59] #9 clean up --- APRSFormater.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/APRSFormater.cpp b/APRSFormater.cpp index ecd1f68..359ca3e 100644 --- a/APRSFormater.cpp +++ b/APRSFormater.cpp @@ -33,13 +33,6 @@ bool CAPRSFormater::frameToString(std::string& output, CAPRSFrame& frame) return false; } - // std::string path(frame.getPath().size() > 0U ? "," : ""); - // for(auto pathItem : frame.getPath()) { - // path.append(boost::trim_copy(pathItem)).push_back(','); - // } - - // boost::trim_right_if(path, [](char c){ return c == ','; }); - 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", From 7f3058155ccfd764e08e4c2c99d77d30cf161bb6 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 9 Jan 2022 07:00:28 +0100 Subject: [PATCH 33/59] update file assocs --- .vscode/settings.json | 57 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a75b395..f39f63c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,7 +16,62 @@ "list": "cpp", "vector": "cpp", "optional": "cpp", - "stop_token": "cpp" + "stop_token": "cpp", + "any": "cpp", + "bitset": "cpp", + "memory": "cpp", + "random": "cpp", + "future": "cpp", + "memory_resource": "cpp", + "ranges": "cpp", + "mutex": "cpp", + "condition_variable": "cpp", + "forward_list": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "algorithm": "cpp", + "cctype": "cpp", + "cstddef": "cpp", + "cstdlib": "cpp", + "cwchar": "cpp", + "atomic": "cpp", + "bit": "cpp", + "compare": "cpp", + "cstdint": "cpp", + "exception": "cpp", + "functional": "cpp", + "iterator": "cpp", + "string_view": "cpp", + "tuple": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "limits": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "typeinfo": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "concepts": "cpp", + "cstdio": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwctype": "cpp", + "istream": "cpp", + "numbers": "cpp", + "ratio": "cpp", + "semaphore": "cpp", + "streambuf": "cpp", + "system_error": "cpp", + "thread": "cpp", + "typeindex": "cpp", + "variant": "cpp" }, "editor.tokenColorCustomizations": { "textMateRules": [ From 27ce54004670fda527d82b237b00d24bb9935053 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 9 Jan 2022 07:57:27 +0100 Subject: [PATCH 34/59] #9 APRS stuff always uses strings, fix messages not being sent along with GPS --- APRSCollector.cpp | 35 ++++++++++--- APRSCollector.h | 7 ++- APRSEntry.cpp | 1 + APRSHandler.cpp | 53 +++++++------------ APRSHandlerThread.cpp | 2 +- GPSACollector.cpp | 23 ++++++-- GPSACollector.h | 1 + NMEASentenceCollector.cpp | 32 ++++++++---- NMEASentenceCollector.h | 1 + RSMS1AMessageCollector.cpp | 42 +++++++++------ RSMS1AMessageCollector.h | 1 + SentenceCollector.h | 1 + SlowDataCollector.cpp | 6 ++- SlowDataCollector.h | 21 +++++++- SlowDataCollectorThrottle.cpp | 98 +++++++++++++++++++++++++++++++++++ SlowDataCollectorThrottle.h | 46 ++++++++++++++++ 16 files changed, 296 insertions(+), 74 deletions(-) create mode 100644 SlowDataCollectorThrottle.cpp create mode 100644 SlowDataCollectorThrottle.h diff --git a/APRSCollector.cpp b/APRSCollector.cpp index 9f10a18..99fda5f 100644 --- a/APRSCollector.cpp +++ b/APRSCollector.cpp @@ -28,6 +28,7 @@ #include "NMEASentenceCollector.h" #include "GPSACollector.h" #include "RSMS1AMessageCollector.h" +#include "SlowDataCollectorThrottle.h" const unsigned int APRS_CSUM_LENGTH = 4U; const unsigned int APRS_DATA_LENGTH = 300U; @@ -39,14 +40,14 @@ const char APRS_SYMBOL = 'K'; CAPRSCollector::CAPRSCollector() : m_collectors() { - m_collectors.push_back(new CRSMS1AMessageCollector()); - m_collectors.push_back(new CGPSACollector()); - m_collectors.push_back(new CNMEASentenceCollector("$GPRMC")); - m_collectors.push_back(new CNMEASentenceCollector("$GPGGA")); - m_collectors.push_back(new CNMEASentenceCollector("$GPGLL")); - m_collectors.push_back(new CNMEASentenceCollector("$GPVTG")); - m_collectors.push_back(new CNMEASentenceCollector("$GPGSA")); - m_collectors.push_back(new CNMEASentenceCollector("$GPGSV")); + 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() @@ -99,3 +100,21 @@ unsigned int CAPRSCollector::getData(unsigned char dataType, unsigned char* data } return 0U; } + +void CAPRSCollector::getData(std::function 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); + } +} \ No newline at end of file diff --git a/APRSCollector.h b/APRSCollector.h index 27d4eba..2f955bf 100644 --- a/APRSCollector.h +++ b/APRSCollector.h @@ -21,6 +21,7 @@ #define APRSCollector_H #include +#include #include "SlowDataCollector.h" #include "Defs.h" @@ -47,9 +48,13 @@ public: void sync(); unsigned int getData(unsigned char dataType, unsigned char* data, unsigned int length); + + void getData(std::function dataHandler); + + void clock(unsigned int ms); private: - std::vector m_collectors; + std::vector m_collectors; }; #endif diff --git a/APRSEntry.cpp b/APRSEntry.cpp index 2d1c3d3..13d1b1a 100644 --- a/APRSEntry.cpp +++ b/APRSEntry.cpp @@ -106,6 +106,7 @@ void CAPRSEntry::clock(unsigned int ms) { m_linkStatus.clock(ms); m_timer.clock(ms); + m_collector->clock(ms); } bool CAPRSEntry::isOK() diff --git a/APRSHandler.cpp b/APRSHandler.cpp index c8539fc..dcf275f 100644 --- a/APRSHandler.cpp +++ b/APRSHandler.cpp @@ -122,43 +122,28 @@ void CAPRSHandler::writeData(const std::string& callsign, const CAMBEData& data) return; } - // Check the transmission timer - bool ok = entry->isOK(); - if (!ok) { - collector->reset(); - return; - } - - unsigned int length = collector->getData(SLOW_DATA_TYPE_GPS, buffer, 400U); - - if(length == 0U) { - return; - } - - std::string text((char*)buffer, length); - - CAPRSFrame frame; - if(!CAPRSParser::parseFrame(text, frame)) { - collector->reset(); - CLog::logWarning("Failed to parse DPRS Frame : %s", text.c_str()); - return; - } - - // If we already have a q-construct, don't send it on - if(std::any_of(frame.getPath().begin(), frame.getPath().end(), [] (std::string s) { return !s.empty() && s[0] == 'q'; })) { - CLog::logWarning("DPRS Frame already has q construct, not forwarding to APRS-IS: %s", text.c_str()); - return; - } + collector->getData([=](const std::string& text) + { + CAPRSFrame frame; + if(!CAPRSParser::parseFrame(text, frame)) { + CLog::logWarning("Failed to parse DPRS Frame : %s", text.c_str()); + return; + } - frame.getPath().push_back("qAR"); - frame.getPath().push_back(CStringUtils::string_format("%s-%s", entry->getCallsign().c_str(), entry->getBand().c_str())); - - std::string output ; - CAPRSFormater::frameToString(output, frame); + // If we already have a q-construct, don't send it on + if(std::any_of(frame.getPath().begin(), frame.getPath().end(), [] (std::string s) { return !s.empty() && s[0] == 'q'; })) { + CLog::logWarning("DPRS Frame already has q construct, not forwarding to APRS-IS: %s", text.c_str()); + return; + } - m_thread->write(frame); + frame.getPath().push_back("qAR"); + frame.getPath().push_back(CStringUtils::string_format("%s-%s", entry->getCallsign().c_str(), entry->getBand().c_str())); + + std::string output ; + CAPRSFormater::frameToString(output, frame); - collector->reset(); + m_thread->write(frame); + }); } void CAPRSHandler::writeStatus(const std::string& callsign, const std::string status) diff --git a/APRSHandlerThread.cpp b/APRSHandlerThread.cpp index 2c84c65..7b410a3 100644 --- a/APRSHandlerThread.cpp +++ b/APRSHandlerThread.cpp @@ -216,8 +216,8 @@ void CAPRSHandlerThread::write(CAPRSFrame& frame) std::string frameString; if(CAPRSFormater::frameToString(frameString, frame)) { - boost::trim_if(frameString, [] (char c) { return c == '\r' || c == '\n'; }); // trim all CRLF, we will add our own, just to make sure we get rid of any garbage that might come from slow data + CLog::logTrace("Queued APRS Frame : %s", frameString.c_str()); frameString.append("\r\n"); m_queue.addData(frameString); diff --git a/GPSACollector.cpp b/GPSACollector.cpp index df04ab0..3774713 100644 --- a/GPSACollector.cpp +++ b/GPSACollector.cpp @@ -54,14 +54,14 @@ bool CGPSACollector::isValidGPSA(const std::string& gpsa) unsigned int CGPSACollector::getDataInt(unsigned char * data, unsigned int length) { - if(data == nullptr || length == 0U || getSentence().empty()) + if(data == nullptr || length == 0U) return 0U; - auto aprsFrame = getSentence(); - if(aprsFrame.length() < 11U) + std::string aprsFrame; + + if(!getDataInt(aprsFrame)) return 0U; - aprsFrame = aprsFrame.substr(10).append("\r\n"); auto aprsFrameLen = aprsFrame.length(); if(length < aprsFrameLen) { @@ -74,4 +74,19 @@ unsigned int CGPSACollector::getDataInt(unsigned char * data, unsigned int lengt } return aprsFrameLen; +} + +bool CGPSACollector::getDataInt(std::string& data) +{ + if(getSentence().empty()) + return false; + + data.clear(); + auto aprsFrame = getSentence(); + if(aprsFrame.length() < 11U)//do we have enough data beyond CRC? + return false; + + data = aprsFrame.substr(10); + + return true; } \ No newline at end of file diff --git a/GPSACollector.h b/GPSACollector.h index 056c0ba..2f6d23c 100644 --- a/GPSACollector.h +++ b/GPSACollector.h @@ -30,6 +30,7 @@ public: protected: unsigned int getDataInt(unsigned char * data, unsigned int length); + bool getDataInt(std::string& data); bool isValidSentence(const std::string& sentence); private: diff --git a/NMEASentenceCollector.cpp b/NMEASentenceCollector.cpp index b368060..9caa630 100644 --- a/NMEASentenceCollector.cpp +++ b/NMEASentenceCollector.cpp @@ -76,17 +76,12 @@ unsigned char CNMEASentenceCollector::calcXOR(const std::string& nmea) unsigned int CNMEASentenceCollector::getDataInt(unsigned char * data, unsigned int length) { - if(data == nullptr || length == 0U || getMyCall().empty() || getSentence().empty()) + if(data == nullptr || length == 0U) return 0U; - auto nmea = getSentence(); - fixUpNMEATimeStamp(nmea); - - std::string fromCall = getMyCall(); - CAPRSUtils::dstarCallsignToAPRS(fromCall); - std::string aprsFrame(fromCall); - aprsFrame.append("-5>GPS30,DSTAR*:") - .append(nmea); + std::string aprsFrame; + if(!getDataInt(aprsFrame)) + return 0U; auto aprsFrameLen = aprsFrame.length(); if(length < aprsFrameLen) { @@ -101,6 +96,25 @@ unsigned int CNMEASentenceCollector::getDataInt(unsigned char * data, unsigned i return aprsFrameLen; } +bool CNMEASentenceCollector::getDataInt(std::string& data) +{ + if(getMyCall().empty() || getSentence().empty()) + return false; + + data.clear(); + auto nmea = getSentence(); + fixUpNMEATimeStamp(nmea); + + std::string fromCall = getMyCall(); + CAPRSUtils::dstarCallsignToAPRS(fromCall); + std::string aprsFrame(fromCall); + aprsFrame.append("-5>GPS30,DSTAR*:") + .append(nmea); + + data.assign(aprsFrame); + return true; +} + // When set on manual position Icom radios send 000000.00 as NMEA timestamps // this is a dirty hack to correct this issue. Actually I am not sure about APRS // software being peeky about this except APRS.fi diff --git a/NMEASentenceCollector.h b/NMEASentenceCollector.h index 6368fb5..d6726c5 100644 --- a/NMEASentenceCollector.h +++ b/NMEASentenceCollector.h @@ -30,6 +30,7 @@ public: protected: unsigned int getDataInt(unsigned char * data, unsigned int length); + bool getDataInt(std::string& data); bool isValidSentence(const std::string& sentence); private: diff --git a/RSMS1AMessageCollector.cpp b/RSMS1AMessageCollector.cpp index 03c36dd..81ae9ca 100644 --- a/RSMS1AMessageCollector.cpp +++ b/RSMS1AMessageCollector.cpp @@ -54,9 +54,32 @@ bool CRSMS1AMessageCollector::isValidMsg(const std::string& msg) unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned int length) { - if(data == nullptr || length == 0U || getSentence().empty()) + if(data == nullptr || length == 0U) return 0U; + std::string aprsFrame; + if(!getDataInt(aprsFrame)) + return 0U; + + auto aprsFrameLen = aprsFrame.length(); + + if(length < aprsFrameLen) { + CLog::logDebug("Not enough space to copy RSMS1A message frame"); + return 0U; + } + + for(unsigned int i = 0U; i < aprsFrameLen; i++){ + data[i] = aprsFrame[i]; + } + + return aprsFrameLen; +} + +bool CRSMS1AMessageCollector::getDataInt(std::string& data) +{ + if(getSentence().empty()) + return false; + std::string sender, recipient, body, sentence; sentence = getSentence(); auto parseRes = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, sentence); @@ -65,25 +88,14 @@ unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned (unsigned char *)sentence.c_str(), sentence.length()); if(parseRes == RSMS_FAIL) - return 0U; + return false; CAPRSUtils::dstarCallsignToAPRS(sender); CAPRSUtils::dstarCallsignToAPRS(recipient); recipient.resize(9U, ' '); auto seqNum = rand() % 0xFFFFFU; - auto aprsFrame = CStringUtils::string_format("%s-5>APDPRS,DSTAR*::%s:%s{%05X", sender.c_str(), recipient.c_str(), body.c_str(), seqNum); + CStringUtils::string_format_in_place(data, "%s-5>APDPRS,DSTAR*::%s:%s{%05X", sender.c_str(), recipient.c_str(), body.c_str(), seqNum); - auto aprsFrameLen = aprsFrame.length(); - - if(length < aprsFrameLen) { - CLog::logDebug("Not enough space to copy GPS-A APRS frame"); - return 0U; - } - - for(unsigned int i = 0U; i < aprsFrameLen; i++){ - data[i] = aprsFrame[i]; - } - - return aprsFrameLen; + return true; } \ No newline at end of file diff --git a/RSMS1AMessageCollector.h b/RSMS1AMessageCollector.h index c7bbe24..f741e92 100644 --- a/RSMS1AMessageCollector.h +++ b/RSMS1AMessageCollector.h @@ -30,6 +30,7 @@ public: protected: unsigned int getDataInt(unsigned char * data, unsigned int length); + bool getDataInt(std::string& data); bool isValidSentence(const std::string& sentence); private: diff --git a/SentenceCollector.h b/SentenceCollector.h index d8a6c3d..2c3b1b9 100644 --- a/SentenceCollector.h +++ b/SentenceCollector.h @@ -32,6 +32,7 @@ protected: bool addData(const unsigned char * data); virtual unsigned int getDataInt(unsigned char * data, unsigned int length) = 0; + virtual bool getDataInt(std::string& data) = 0; static void dstarCallsignToAPRS(std::string& call); std::string getSentence(); diff --git a/SlowDataCollector.cpp b/SlowDataCollector.cpp index 004226e..b9caad0 100644 --- a/SlowDataCollector.cpp +++ b/SlowDataCollector.cpp @@ -85,10 +85,14 @@ unsigned int CSlowDataCollector::getData(unsigned char * data, unsigned int leng return getDataInt(data, length); } +bool CSlowDataCollector::getData(std::string& data) +{ + return getDataInt(data); +} + void CSlowDataCollector::reset() { m_state = SS_FIRST; - m_myCall.clear(); resetInt(); } diff --git a/SlowDataCollector.h b/SlowDataCollector.h index 03d4de1..f2389f9 100644 --- a/SlowDataCollector.h +++ b/SlowDataCollector.h @@ -24,7 +24,23 @@ #include "DStarDefines.h" #include "Defs.h" -class CSlowDataCollector +class ISlowDataCollector +{ +public: + virtual ~ISlowDataCollector() { } ; + + virtual std::string getMyCall() const = 0; + virtual void setMyCall(const std::string& mycall) = 0; + virtual bool writeData(const unsigned char* data) = 0; + virtual void sync() = 0; + virtual unsigned int getData(unsigned char* data, unsigned int length) = 0; + virtual bool getData(std::string& data) = 0; + virtual void reset() = 0; + virtual unsigned char getDataType() = 0; + virtual void clock(unsigned int ms) = 0; +}; + +class CSlowDataCollector : public ISlowDataCollector { public: CSlowDataCollector(unsigned char slowDataType); @@ -35,12 +51,15 @@ public: bool writeData(const unsigned char* data); void sync(); unsigned int getData(unsigned char* data, unsigned int length); + bool getData(std::string& data); void reset(); unsigned char getDataType(); + void clock(unsigned int) { }; protected: virtual bool addData(const unsigned char* data) = 0; virtual unsigned int getDataInt(unsigned char* data, unsigned int length) = 0; + virtual bool getDataInt(std::string& data) = 0; virtual void resetInt() { } private: diff --git a/SlowDataCollectorThrottle.cpp b/SlowDataCollectorThrottle.cpp new file mode 100644 index 0000000..8d97fe1 --- /dev/null +++ b/SlowDataCollectorThrottle.cpp @@ -0,0 +1,98 @@ +/* + * 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 + +#include "SlowDataCollectorThrottle.h" + +CSlowDataCollectorThrottle::CSlowDataCollectorThrottle(ISlowDataCollector* collector, unsigned int timeout) : +m_collector(collector), +m_timer(1000U, timeout), +m_isFirst(true) +{ + assert(collector != nullptr); +} + +CSlowDataCollectorThrottle::~CSlowDataCollectorThrottle() +{ + delete m_collector; +} + +std::string CSlowDataCollectorThrottle::getMyCall() const +{ + return m_collector->getMyCall(); +} + +void CSlowDataCollectorThrottle::setMyCall(const std::string& mycall) +{ + m_isFirst = true; + m_collector->setMyCall(mycall); +} +bool CSlowDataCollectorThrottle::writeData(const unsigned char* data) +{ + m_isComplete = false; + bool complete = m_collector->writeData(data); + if(complete){ + if(m_isFirst) { + m_isFirst = false; + m_isComplete = true; + m_timer.start(); + return true; + } + + if(m_timer.hasExpired()) { + m_isComplete = true; + m_timer.start(); + return true; + } + } + + return false; +} +void CSlowDataCollectorThrottle::sync() +{ + m_collector->sync(); +} +unsigned int CSlowDataCollectorThrottle::getData(unsigned char* data, unsigned int length) +{ + if(m_isComplete) + return m_collector->getData(data, length); + + return 0U; +} +bool CSlowDataCollectorThrottle::getData(std::string& data) +{ + if(m_isComplete) + return m_collector->getData(data); + + return false; +} +void CSlowDataCollectorThrottle::reset() +{ + m_timer.start(); + m_collector->reset(); +} +unsigned char CSlowDataCollectorThrottle::getDataType() +{ + return m_collector->getDataType(); +} + +void CSlowDataCollectorThrottle::clock(unsigned int ms) +{ + m_timer.clock(ms); +} \ No newline at end of file diff --git a/SlowDataCollectorThrottle.h b/SlowDataCollectorThrottle.h new file mode 100644 index 0000000..f7fbd23 --- /dev/null +++ b/SlowDataCollectorThrottle.h @@ -0,0 +1,46 @@ +/* + * 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 + +#include "SlowDataCollector.h" +#include "Timer.h" + +class CSlowDataCollectorThrottle : public ISlowDataCollector +{ +public: + CSlowDataCollectorThrottle(ISlowDataCollector* collector, unsigned int timeout); + ~CSlowDataCollectorThrottle(); + std::string getMyCall() const; + void setMyCall(const std::string& mycall); + bool writeData(const unsigned char* data); + void sync(); + unsigned int getData(unsigned char* data, unsigned int length); + bool getData(std::string& data); + void reset(); + unsigned char getDataType(); + void clock(unsigned int ms); + +private: + ISlowDataCollector* m_collector; + CTimer m_timer; + bool m_isFirst; + bool m_isComplete; +}; \ No newline at end of file From 4b7dead48367b3c14c607ddba38952d46ee8218e Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 9 Jan 2022 08:04:45 +0100 Subject: [PATCH 35/59] #9 Do not block on read for 10 seconds --- APRSHandlerThread.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/APRSHandlerThread.cpp b/APRSHandlerThread.cpp index 7b410a3..aab86cb 100644 --- a/APRSHandlerThread.cpp +++ b/APRSHandlerThread.cpp @@ -33,6 +33,7 @@ // #define DUMP_TX const unsigned int APRS_TIMEOUT = 10U; +const unsigned int APRS_READ_TIMEOUT = 1U; CAPRSHandlerThread::CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port) : CThread(), @@ -151,7 +152,7 @@ void* CAPRSHandlerThread::Entry() } { std::string line; - int length = m_socket.readLine(line, APRS_TIMEOUT); + int length = m_socket.readLine(line, APRS_READ_TIMEOUT); /*if (length == 0) CLog::logWarning(("No response from the APRS server after %u seconds", APRS_TIMEOUT);*/ From 25a439d16c7c588c52b1182a1e7252ec4f471c38 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 9 Jan 2022 18:09:42 +0100 Subject: [PATCH 36/59] #9 Handle keeap alives from server --- APRSHandlerThread.cpp | 28 ++++++++++++++++------------ APRSHandlerThread.h | 1 + 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/APRSHandlerThread.cpp b/APRSHandlerThread.cpp index aab86cb..bd67dcd 100644 --- a/APRSHandlerThread.cpp +++ b/APRSHandlerThread.cpp @@ -34,6 +34,7 @@ const unsigned int APRS_TIMEOUT = 10U; const unsigned int APRS_READ_TIMEOUT = 1U; +const unsigned int APRS_KEEP_ALIVE_TIMEOUT = 60U; CAPRSHandlerThread::CAPRSHandlerThread(const std::string& callsign, const std::string& password, const std::string& address, const std::string& hostname, unsigned int port) : CThread(), @@ -45,6 +46,7 @@ m_queue(20U), m_exit(false), m_connected(false), m_reconnectTimer(1000U), +m_keepAliveTimer(1000U, APRS_KEEP_ALIVE_TIMEOUT), m_tries(0U), m_APRSReadCallbacks(), m_filter(), @@ -72,6 +74,7 @@ m_queue(20U), m_exit(false), m_connected(false), m_reconnectTimer(1000U), +m_keepAliveTimer(1000U, APRS_KEEP_ALIVE_TIMEOUT), m_tries(0U), m_APRSReadCallbacks(), m_filter(filter), @@ -121,6 +124,7 @@ void* CAPRSHandlerThread::Entry() } try { + m_keepAliveTimer.start(); while (!m_exit) { if (!m_connected) { if (m_reconnectTimer.isRunning() && m_reconnectTimer.hasExpired()) { @@ -131,6 +135,9 @@ void* CAPRSHandlerThread::Entry() CLog::logInfo("Reconnect attempt to the APRS server has failed"); startReconnectionTimer(); } + else { + m_keepAliveTimer.start(); + } } } @@ -154,22 +161,18 @@ void* CAPRSHandlerThread::Entry() std::string line; int length = m_socket.readLine(line, APRS_READ_TIMEOUT); - /*if (length == 0) - CLog::logWarning(("No response from the APRS server after %u seconds", APRS_TIMEOUT);*/ - - if (length < 0) { + if (length < 0 || m_keepAliveTimer.hasExpired()) { m_connected = false; m_socket.close(); CLog::logError("Error when reading from the APRS server"); startReconnectionTimer(); } - - if(line.length() > 0 && line[0] != '#') - CLog::logDebug("Received APRS Frame : %s", line.c_str()); - - if(length > 0 && line[0] != '#'//check if we have something and if that something is an APRS frame - && m_APRSReadCallbacks.size() > 0U)//do we have someone wanting an APRS Frame? - { + else if(length > 0 && line[0] == '#') { + m_keepAliveTimer.start(); + } + else if(line.length() > 0 && line[0] != '#') { + m_keepAliveTimer.start(); + CLog::logDebug("APRS <== %s", line.c_str()); CAPRSFrame readFrame; if(CAPRSParser::parseFrame(line, readFrame)) { for(auto cb : m_APRSReadCallbacks) { @@ -179,7 +182,6 @@ void* CAPRSHandlerThread::Entry() } } } - } } @@ -240,10 +242,12 @@ void CAPRSHandlerThread::stop() void CAPRSHandlerThread::clock(unsigned int ms) { m_reconnectTimer.clock(ms); + m_keepAliveTimer.clock(ms); } bool CAPRSHandlerThread::connect() { + m_socket.close(); bool ret = m_socket.open(); if (!ret) return false; diff --git a/APRSHandlerThread.h b/APRSHandlerThread.h index 73642ba..01410de 100644 --- a/APRSHandlerThread.h +++ b/APRSHandlerThread.h @@ -58,6 +58,7 @@ private: bool m_exit; bool m_connected; CTimer m_reconnectTimer; + CTimer m_keepAliveTimer; unsigned int m_tries; std::vector m_APRSReadCallbacks; std::string m_filter; From 761fca2a46347b5fe639e11b622f718e2fc9bd01 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 9 Jan 2022 18:27:05 +0100 Subject: [PATCH 37/59] #9 rename namespace --- Tests/RSMS1AMessageBuilder/buildMessage.cpp | 2 +- Tests/RSMS1AMessageBuilder/parseMessage.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/RSMS1AMessageBuilder/buildMessage.cpp b/Tests/RSMS1AMessageBuilder/buildMessage.cpp index 6312e71..aefcb68 100644 --- a/Tests/RSMS1AMessageBuilder/buildMessage.cpp +++ b/Tests/RSMS1AMessageBuilder/buildMessage.cpp @@ -20,7 +20,7 @@ #include "../../RSMS1AMessageBuilder.h" -namespace RSMS1AMessageBuilder +namespace RSMS1AMessageBuilderTests { class RSMS1AMessageBuilder_buildMessage : public ::testing::Test { diff --git a/Tests/RSMS1AMessageBuilder/parseMessage.cpp b/Tests/RSMS1AMessageBuilder/parseMessage.cpp index ea155ac..5e1c82d 100644 --- a/Tests/RSMS1AMessageBuilder/parseMessage.cpp +++ b/Tests/RSMS1AMessageBuilder/parseMessage.cpp @@ -20,7 +20,7 @@ #include "../../RSMS1AMessageBuilder.h" -namespace RSMS1AMessageBuilder +namespace RSMS1AMessageBuilderTests { class RSMS1AMessageBuilder_parseMessage : public ::testing::Test { From da924acb7ca500b8667e3e6595985b18751b0481 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 9 Jan 2022 22:19:42 +0100 Subject: [PATCH 38/59] #9 It works ! Still need to figure out interleaved data and acks --- APRSUnit.cpp | 88 +++++++++++++++++---------------- APRSUnit.h | 9 +++- APRStoDPRS.cpp | 68 +++++++++++++++++++++++++ APRStoDPRS.h | 33 +++++++++++++ Tests/APRStoDPRS/aprsToDPRS.cpp | 64 ++++++++++++++++++++++++ 5 files changed, 218 insertions(+), 44 deletions(-) create mode 100644 APRStoDPRS.cpp create mode 100644 APRStoDPRS.h create mode 100644 Tests/APRStoDPRS/aprsToDPRS.cpp diff --git a/APRSUnit.cpp b/APRSUnit.cpp index b46a598..4dd4b1f 100644 --- a/APRSUnit.cpp +++ b/APRSUnit.cpp @@ -21,15 +21,20 @@ #include "APRSUnit.h" #include "APRSFormater.h" #include "StringUtils.h" -#include "APRSUtils.h" -#include "SlowDataEncoder.h" +#include "APRStoDPRS.h" CAPRSUnit::CAPRSUnit(IRepeaterCallback * repeaterHandler) : m_frameBuffer(20U), m_status(APS_IDLE), m_repeaterHandler(repeaterHandler), m_headerData(nullptr), -m_timer(1000U, 2U) +m_slowData(nullptr), +m_out(0U), +m_seq(0U), +m_totalNeeded(0U), +m_timer(1000U, 2U), +m_dprs(), +m_start() { m_timer.start(); } @@ -40,75 +45,72 @@ void CAPRSUnit::writeFrame(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()) { + m_status = APS_TRANSMIT; auto frame = m_frameBuffer.front(); + m_frameBuffer.pop_front(); - m_id = CHeaderData::createId(); + m_headerData = new CHeaderData(); + CAPRSToDPRS::aprsToDPRS(m_dprs, *m_headerData, *frame); - m_headerData = new CHeaderData(); - m_headerData->setMyCall1(frame->getSource()); - m_headerData->setMyCall2("APRS"); - m_headerData->setYourCall("CQCQCQ "); - m_headerData->setId(m_id); + m_slowData = new CSlowDataEncoder(); + // icom rs-ms1 seem to not support messaiging mixed with other slow data + // send the message on its own for now + // m_slowData->setHeaderData(*m_headerData); + m_slowData->setGPSData(m_dprs); + // m_slowData->setTextData("APRS to DPRS"); - m_repeaterHandler->process(*m_headerData, DIR_INCOMING, AS_INFO); - - m_status = APS_TRANSMIT; - } + m_totalNeeded = (m_slowData->getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U; - if(m_status == APS_TRANSMIT && !m_frameBuffer.empty()) - { - auto frame = m_frameBuffer.front(); - std::string frameString; - CAPRSFormater::frameToString(frameString, *frame); - boost::trim_right_if(frameString, [](char c) { return c == '\n' || c == '\r'; }); - frameString.push_back('\r'); + m_repeaterHandler->process(*m_headerData, DIR_INCOMING, AS_INFO); - std::string crc = CStringUtils::string_format("$$CRC%04X", CAPRSUtils::calcGPSAIcomCRC(frameString)); - frameString.insert(0, crc); + m_out = 0U; + m_seq = 0U; - CSlowDataEncoder encoder; - encoder.setHeaderData(*m_headerData); - encoder.setGPSData(frameString); - encoder.setTextData("APRS to DPRS"); + m_start = std::chrono::high_resolution_clock::now(); + return; + } - CAMBEData data; - data.setId(m_id); + if(m_status == APS_TRANSMIT) { + unsigned int needed = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_start).count(); + needed /= DSTAR_FRAME_TIME_MS; - unsigned int out = 0U; - unsigned int dataOut = 0U; - unsigned int needed = (encoder.getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U; unsigned char buffer[DV_FRAME_LENGTH_BYTES]; - while (dataOut < needed) { - data.setSeq(out); + 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 (out == 0U) { + if (m_seq == 0U) { ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); } else { - encoder.getInterleavedData(buffer + VOICE_FRAME_LENGTH_BYTES); - dataOut++; + 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); - out++; - if (out == 21U) out = 0U; + m_seq++; + if (m_seq == 21U) m_seq = 0U; } - m_frameBuffer.pop_front(); - delete frame; - m_status = APS_IDLE; - m_timer.start(); + if(m_out >= m_totalNeeded) { + m_status = APS_IDLE; + delete m_headerData; + delete m_slowData; + } } } \ No newline at end of file diff --git a/APRSUnit.h b/APRSUnit.h index 454c4cd..466f6d5 100644 --- a/APRSUnit.h +++ b/APRSUnit.h @@ -20,10 +20,12 @@ #include #include +#include #include "APRSFrame.h" #include "RepeaterCallback.h" #include "Timer.h" +#include "SlowDataEncoder.h" enum APRSUNIT_STATUS { APS_IDLE, @@ -43,9 +45,14 @@ private: boost::circular_buffer m_frameBuffer; APRSUNIT_STATUS m_status; IRepeaterCallback * m_repeaterHandler; - unsigned int m_id; CHeaderData * m_headerData; + CSlowDataEncoder * m_slowData; + unsigned int m_out; + unsigned int m_seq; + unsigned int m_totalNeeded; CTimer m_timer; + std::string m_dprs; + std::chrono::high_resolution_clock::time_point m_start; }; diff --git a/APRStoDPRS.cpp b/APRStoDPRS.cpp new file mode 100644 index 0000000..423932f --- /dev/null +++ b/APRStoDPRS.cpp @@ -0,0 +1,68 @@ +/* + * 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 + +#include "APRStoDPRS.h" +#include "Log.h" +#include "RSMS1AMessageBuilder.h" + +bool CAPRSToDPRS::aprsToDPRS(std::string& dprs, CHeaderData& header, CAPRSFrame& frame) +{ + dprs.clear(); + switch (frame.getType()) + { + case APFT_MESSAGE : + return messageToDPRS(dprs, header, frame); + default: + break; + } + + return false; +} + +bool CAPRSToDPRS::messageToDPRS(std::string& dprs, 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.length() == 0U) { + 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); + + + 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); + + return true; +} \ No newline at end of file diff --git a/APRStoDPRS.h b/APRStoDPRS.h new file mode 100644 index 0000000..2e185d7 --- /dev/null +++ b/APRStoDPRS.h @@ -0,0 +1,33 @@ +/* + * 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 + +#include "HeaderData.h" +#include "APRSFrame.h" + +class CAPRSToDPRS +{ +public: + static bool aprsToDPRS(std::string& dprs, CHeaderData& header, CAPRSFrame& frame); + +private: + static bool messageToDPRS(std::string& drps, CHeaderData& header, CAPRSFrame& frame); +}; \ No newline at end of file diff --git a/Tests/APRStoDPRS/aprsToDPRS.cpp b/Tests/APRStoDPRS/aprsToDPRS.cpp new file mode 100644 index 0000000..94f8b29 --- /dev/null +++ b/Tests/APRStoDPRS/aprsToDPRS.cpp @@ -0,0 +1,64 @@ +/* + * 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 + +#include "../../APRStoDPRS.h" + +namespace APRStoDPRSTests +{ + class APRStoDPRS_aprsToDPRS : public ::testing::Test { + + }; + + TEST_F(APRStoDPRS_aprsToDPRS, validMessage) + { + CAPRSFrame frame("KC3FRA", "APRS", {"WIDE1-1", "WIDE2-2"}, ":F4FXL :Salut, comment vas tu?", APFT_MESSAGE); + + std::string dprs; + CHeaderData header; + bool ret = CAPRSToDPRS::aprsToDPRS(dprs, header, frame); + + EXPECT_TRUE(ret); + EXPECT_STREQ(dprs.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\r"); + } + + TEST_F(APRStoDPRS_aprsToDPRS, validMessageRecipientWithSSID) + { + CAPRSFrame frame("KC3FRA", "APRS", {"WIDE1-1", "WIDE2-2"}, ":F4FXL-7 :Salut, comment vas tu?", APFT_MESSAGE); + + std::string dprs; + CHeaderData header; + bool ret = CAPRSToDPRS::aprsToDPRS(dprs, header, frame); + + EXPECT_TRUE(ret); + EXPECT_STREQ(dprs.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\r"); + } + + TEST_F(APRStoDPRS_aprsToDPRS, emptyRecipient) + { + CAPRSFrame frame("KC3FRA", "APRS", {"WIDE1-1", "WIDE2-2"}, ": :Salut, comment vas tu?", APFT_MESSAGE); + + std::string dprs; + CHeaderData header; + bool ret = CAPRSToDPRS::aprsToDPRS(dprs, header, frame); + + EXPECT_FALSE(ret); + EXPECT_STREQ(dprs.c_str(), ""); + } +} From 5bcaef8f7a1b1d8a1c03b9992c2f795af103b93f Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 10 Jan 2022 20:18:47 +0100 Subject: [PATCH 39/59] #9 Add more logging --- APRSHandlerThread.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/APRSHandlerThread.cpp b/APRSHandlerThread.cpp index bd67dcd..b13e99b 100644 --- a/APRSHandlerThread.cpp +++ b/APRSHandlerThread.cpp @@ -127,9 +127,11 @@ void* CAPRSHandlerThread::Entry() m_keepAliveTimer.start(); while (!m_exit) { if (!m_connected) { + Sleep(100U); if (m_reconnectTimer.isRunning() && m_reconnectTimer.hasExpired()) { m_reconnectTimer.stop(); + CLog::logDebug("Trying to reconnect to the APRS server"); m_connected = connect(); if (!m_connected) { CLog::logInfo("Reconnect attempt to the APRS server has failed"); @@ -153,7 +155,7 @@ void* CAPRSHandlerThread::Entry() if (!ret) { m_connected = false; m_socket.close(); - CLog::logInfo("Connection to the APRS thread has failed"); + CLog::logInfo("Error when writing to the APRS server"); startReconnectionTimer(); } } @@ -284,7 +286,7 @@ bool CAPRSHandlerThread::connect() return false; } if (length < 0) { - CLog::logInfo("Error when reading from the APRS server"); + CLog::logInfo("Error when reading from the APRS server (connect)"); m_socket.close(); return false; } @@ -303,6 +305,8 @@ void CAPRSHandlerThread::startReconnectionTimer() if (m_tries > 10U) m_tries = 10U; + CLog::logDebug("Next APRS reconnection try in %u minute", m_tries); + m_reconnectTimer.setTimeout(m_tries * 60U); m_reconnectTimer.start(); } From eada741467a8e346ce03e2e9321aefa8616ad822 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 11 Jan 2022 20:07:07 +0100 Subject: [PATCH 40/59] remove test filter --- APRSHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/APRSHandler.cpp b/APRSHandler.cpp index dcf275f..4d6b81a 100644 --- a/APRSHandler.cpp +++ b/APRSHandler.cpp @@ -47,7 +47,7 @@ m_idFrameProvider(nullptr) assert(!gateway.empty()); assert(!password.empty()); - m_thread = new CAPRSHandlerThread(gateway, password, address, hostname, port, "m/20"); + m_thread = new CAPRSHandlerThread(gateway, password, address, hostname, port); m_gateway = gateway; m_gateway = m_gateway.substr(0, LONG_CALLSIGN_LENGTH - 1U); From e4e4056af0f44080bf0243535416849d5c28110e Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 12 Jan 2022 22:31:27 +0100 Subject: [PATCH 41/59] #9 remove debug log --- GPSACollector.cpp | 1 + RSMS1AMessageCollector.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/GPSACollector.cpp b/GPSACollector.cpp index 3774713..ccbe4a6 100644 --- a/GPSACollector.cpp +++ b/GPSACollector.cpp @@ -37,6 +37,7 @@ CSentenceCollector(SLOW_DATA_TYPE_GPS, "$$CRC", '\x0D') bool CGPSACollector::isValidSentence(const std::string& sentence) { + CLog::logDebug("GPSA"); return isValidGPSA(sentence); } diff --git a/RSMS1AMessageCollector.cpp b/RSMS1AMessageCollector.cpp index 81ae9ca..aa62ee7 100644 --- a/RSMS1AMessageCollector.cpp +++ b/RSMS1AMessageCollector.cpp @@ -48,6 +48,7 @@ bool CRSMS1AMessageCollector::isValidSentence(const std::string& sentence) bool CRSMS1AMessageCollector::isValidMsg(const std::string& msg) { + CLog::logDebug("RS-MS1A"); // checking validity involves parsing, so we do minumum checks here. Big chekc done in getDataInt return !msg.empty() && boost::starts_with(msg, "$$Msg"); } From 686134060d17b929c8d2a0497b936cc157cde2e8 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 12 Jan 2022 22:31:44 +0100 Subject: [PATCH 42/59] #9 clean up --- APRSCollector.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/APRSCollector.cpp b/APRSCollector.cpp index 99fda5f..afba4d0 100644 --- a/APRSCollector.cpp +++ b/APRSCollector.cpp @@ -30,13 +30,6 @@ #include "RSMS1AMessageCollector.h" #include "SlowDataCollectorThrottle.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_collectors() { @@ -68,6 +61,7 @@ void CAPRSCollector::writeHeader(const std::string& callsign) bool CAPRSCollector::writeData(const unsigned char* data) { bool ret = false; + for(auto collector : m_collectors) { bool ret2 = collector->writeData(data); ret = ret || ret2; From 1314cde26aac3c03e74aa102e32db6cad50bbaae Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 12 Jan 2022 22:32:05 +0100 Subject: [PATCH 43/59] #9 clean up --- APRStoDPRS.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/APRStoDPRS.cpp b/APRStoDPRS.cpp index 423932f..8b524a1 100644 --- a/APRStoDPRS.cpp +++ b/APRStoDPRS.cpp @@ -46,7 +46,7 @@ bool CAPRSToDPRS::messageToDPRS(std::string& dprs, CHeaderData& header, CAPRSFra // extract recipient auto recipient = boost::trim_copy(frameBody.substr(1, 9)); - if(recipient.length() == 0U) { + if(recipient.empty()) { CLog::logDebug("APRS message has no recipient"); return false; } @@ -54,7 +54,7 @@ bool CAPRSToDPRS::messageToDPRS(std::string& dprs, CHeaderData& header, CAPRSFra 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()); From 2e64428ccd341a7734f505fe1ec530de434ca3fe Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 12 Jan 2022 22:33:03 +0100 Subject: [PATCH 44/59] #9 fix interleaved data, still need a proper unittest --- APRSUnit.cpp | 7 +++---- SlowDataEncoder.cpp | 33 ++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/APRSUnit.cpp b/APRSUnit.cpp index 4dd4b1f..49e7f4b 100644 --- a/APRSUnit.cpp +++ b/APRSUnit.cpp @@ -60,11 +60,10 @@ void CAPRSUnit::clock(unsigned int ms) CAPRSToDPRS::aprsToDPRS(m_dprs, *m_headerData, *frame); m_slowData = new CSlowDataEncoder(); - // icom rs-ms1 seem to not support messaiging mixed with other slow data - // send the message on its own for now - // m_slowData->setHeaderData(*m_headerData); + + m_slowData->setHeaderData(*m_headerData); m_slowData->setGPSData(m_dprs); - // m_slowData->setTextData("APRS to DPRS"); + m_slowData->setTextData("APRS to DPRS"); m_totalNeeded = (m_slowData->getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U; diff --git a/SlowDataEncoder.cpp b/SlowDataEncoder.cpp index 2dd3953..54be29b 100644 --- a/SlowDataEncoder.cpp +++ b/SlowDataEncoder.cpp @@ -17,6 +17,7 @@ #include "SlowDataEncoder.h" #include "CCITTChecksum.h" #include "DStarDefines.h" +#include "Utils.h" const unsigned int SLOW_DATA_BLOCK_SIZE = 6U; @@ -130,14 +131,14 @@ void CSlowDataEncoder::getInterleavedData(unsigned char* data) getHeaderData(data); else { buildInterleavedData(); - getData(m_interleavedData, data, m_interleavedPtr, m_gpsDataFullSize); + getData(m_interleavedData, data, m_interleavedPtr, m_interleavedDataFullSize); } } void CSlowDataEncoder::buildInterleavedData() { //first build interleaved data if we do not have it - if(!m_interleavedData) + if(m_interleavedData == nullptr) { getInterleavedDataLength(); m_interleavedData = new unsigned char[m_interleavedDataFullSize]; @@ -145,36 +146,38 @@ void CSlowDataEncoder::buildInterleavedData() unsigned int textPtr = 0U; unsigned int gpsPtr = 0U; - unsigned int headerPtr = 0U; //now proceed with data copying, according to this document http://www.qsl.net/kb9mwr/projects/dv/dstar/Slow%20Data.pdf - if(m_textData && m_gpsData){ - for(unsigned int interleavedPtr = 0; interleavedPtr < m_interleavedDataFullSize; interleavedPtr += SLOW_DATA_BLOCK_SIZE){ + if(m_textData != nullptr && m_gpsData != nullptr){ + unsigned int interleavedPtr = 0; + while(textPtr < TEXT_SIZE || gpsPtr < m_gpsDataSize){ if(textPtr < TEXT_SIZE && ((interleavedPtr / SLOW_DATA_BLOCK_SIZE) & 0x01U) == 0) { ::memcpy(m_interleavedData + interleavedPtr, m_textData + textPtr, SLOW_DATA_BLOCK_SIZE); textPtr += SLOW_DATA_BLOCK_SIZE; } - else if(gpsPtr < m_gpsDataSize){ + else if(gpsPtr < m_gpsDataSize) { ::memcpy(m_interleavedData + interleavedPtr, m_gpsData + gpsPtr, SLOW_DATA_BLOCK_SIZE); gpsPtr += SLOW_DATA_BLOCK_SIZE; } - else if(m_headerData && headerPtr < HEADER_SIZE){ - ::memcpy(m_interleavedData + interleavedPtr, m_headerData + headerPtr, SLOW_DATA_BLOCK_SIZE); - headerPtr += SLOW_DATA_BLOCK_SIZE; - } + interleavedPtr += SLOW_DATA_BLOCK_SIZE; + } + + if(m_headerData != nullptr) { + //append header dat in one block at the end + ::memcpy(m_interleavedData + interleavedPtr, m_headerData, HEADER_SIZE); } } - else if(m_textData && !m_gpsData && m_headerData){ + else if(m_textData != nullptr && m_gpsData == nullptr && m_headerData != nullptr){ //according to above doc, header and text are not interleaved, just on after the other. filler bytes between resync bytes. ::memcpy(m_interleavedData, m_textData, SLOW_DATA_FULL_BLOCK_SIZE); ::memcpy(m_interleavedData + SLOW_DATA_FULL_BLOCK_SIZE, m_headerData, SLOW_DATA_FULL_BLOCK_SIZE); } - else if(!m_textData && m_gpsData && m_headerData){ + else if(m_textData == nullptr && m_gpsData != nullptr && m_headerData != nullptr){ //could not find any spec about this particular case, let's put the data one after the other ::memcpy(m_interleavedData, m_gpsData, SLOW_DATA_FULL_BLOCK_SIZE); - ::memcpy(m_interleavedData + SLOW_DATA_FULL_BLOCK_SIZE, m_headerData, SLOW_DATA_FULL_BLOCK_SIZE); + ::memcpy(m_interleavedData + SLOW_DATA_FULL_BLOCK_SIZE, m_headerData, HEADER_SIZE); } } } @@ -347,8 +350,8 @@ void CSlowDataEncoder::setGPSData(const std::string& gpsData) m_gpsData = new unsigned char[m_gpsDataFullSize]; ::memset(m_gpsData, 'f', m_gpsDataFullSize); - for(gpsDataPos = 0; gpsDataPos < m_gpsDataFullSize;){ - unsigned int dataLen = gpsDataStrLen - strPos < 5U ? gpsDataStrLen - strPos : 5U; + for(gpsDataPos = 0; gpsDataPos < m_gpsDataSize;){ + unsigned int dataLen = gpsDataStrLen - strPos< 5U ? gpsDataStrLen - strPos : 5U; m_gpsData[gpsDataPos++] = SLOW_DATA_TYPE_GPS | dataLen; for(unsigned int i = 0U; i < dataLen; i++){ From b9510f764d8485e7bc1887222b524b2625ce9a36 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 07:57:59 +0100 Subject: [PATCH 45/59] #9 make setGPSData a ltillt bit more readable --- SlowDataEncoder.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/SlowDataEncoder.cpp b/SlowDataEncoder.cpp index 54be29b..56442e3 100644 --- a/SlowDataEncoder.cpp +++ b/SlowDataEncoder.cpp @@ -17,7 +17,9 @@ #include "SlowDataEncoder.h" #include "CCITTChecksum.h" #include "DStarDefines.h" -#include "Utils.h" + +// Only works for positive numbers +#define roundUpToMultipleOf(n, m)(((n + m - 1) / m) * m) const unsigned int SLOW_DATA_BLOCK_SIZE = 6U; @@ -137,7 +139,7 @@ void CSlowDataEncoder::getInterleavedData(unsigned char* data) void CSlowDataEncoder::buildInterleavedData() { - //first build interleaved data if we do not have it + //first build interleaved data if we do not h(x + multiple - 1 - (x % multiple))ave it if(m_interleavedData == nullptr) { getInterleavedDataLength(); @@ -177,7 +179,7 @@ void CSlowDataEncoder::buildInterleavedData() else if(m_textData == nullptr && m_gpsData != nullptr && m_headerData != nullptr){ //could not find any spec about this particular case, let's put the data one after the other ::memcpy(m_interleavedData, m_gpsData, SLOW_DATA_FULL_BLOCK_SIZE); - ::memcpy(m_interleavedData + SLOW_DATA_FULL_BLOCK_SIZE, m_headerData, HEADER_SIZE); + ::memcpy(m_interleavedData + SLOW_DATA_FULL_BLOCK_SIZE, m_headerData, SLOW_DATA_FULL_BLOCK_SIZE); } } } @@ -187,9 +189,9 @@ unsigned int CSlowDataEncoder::getInterleavedDataLength() //calculate size (including filler bytes); m_interleavedDataFullSize = 0U; if(m_textData) m_interleavedDataFullSize += TEXT_SIZE; - if(m_headerData) m_interleavedDataFullSize += HEADER_SIZE; - if(m_gpsData) m_interleavedDataFullSize += m_gpsDataSize; - m_interleavedDataFullSize = SLOW_DATA_FULL_BLOCK_SIZE * (1U + ((m_interleavedDataFullSize - 1U) / SLOW_DATA_FULL_BLOCK_SIZE)); + if(m_headerData) m_interleavedDataFullSize += SLOW_DATA_BLOCK_SIZE; + if(m_gpsData) m_interleavedDataFullSize += m_gpsDataFullSize; + m_interleavedDataFullSize = roundUpToMultipleOf(m_interleavedDataFullSize, SLOW_DATA_FULL_BLOCK_SIZE); //SLOW_DATA_FULL_BLOCK_SIZE * (1U + ((m_interleavedDataFullSize - 1U) / SLOW_DATA_FULL_BLOCK_SIZE)); return m_interleavedDataFullSize; } @@ -343,9 +345,9 @@ void CSlowDataEncoder::setGPSData(const std::string& gpsData) if((gpsDataStrLen = gpsData.size()) > 0){ unsigned int gpsDataPos; unsigned int strPos = 0; - m_gpsDataSize = 1U + ((gpsDataStrLen - 1U) / 6U);//to make room for the type bytes - m_gpsDataSize += gpsDataStrLen; - m_gpsDataFullSize = SLOW_DATA_FULL_BLOCK_SIZE * (1U + ((m_gpsDataSize - 1U) / SLOW_DATA_FULL_BLOCK_SIZE)); + unsigned int typeBytesCount = 1U + ((gpsDataStrLen - 1U) / SLOW_DATA_BLOCK_SIZE);//to make room for the type bytes + m_gpsDataSize = typeBytesCount + gpsDataStrLen; + m_gpsDataFullSize = roundUpToMultipleOf(m_gpsDataSize, SLOW_DATA_FULL_BLOCK_SIZE); m_gpsData = new unsigned char[m_gpsDataFullSize]; ::memset(m_gpsData, 'f', m_gpsDataFullSize); From 51101c00592a3a6c86497b6224974b0c4dfc4c19 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 07:58:24 +0100 Subject: [PATCH 46/59] #9 add slow data GPS data tests --- .vscode/tasks.json | 10 ++--- Tests/SlowDataEncoder/GPSData.cpp | 75 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 Tests/SlowDataEncoder/GPSData.cpp diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 168ca95..ac3f100 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,10 +12,7 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] }, { @@ -28,7 +25,10 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] } ] diff --git a/Tests/SlowDataEncoder/GPSData.cpp b/Tests/SlowDataEncoder/GPSData.cpp new file mode 100644 index 0000000..47b3185 --- /dev/null +++ b/Tests/SlowDataEncoder/GPSData.cpp @@ -0,0 +1,75 @@ +/* + * 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 + +#include "../../SlowDataEncoder.h" +#include "../../DStarDefines.h" + +namespace SlowDataEncoderTests +{ + class SlowDataEncoder_gpsData : public ::testing::Test { + + }; + + TEST_F(SlowDataEncoder_gpsData, gpsDataCorrectlySet) + { + CSlowDataEncoder encoder; + encoder.setGPSData("ABCDEFGHIJKLMN"); + + unsigned char buffer[6U]; + + encoder.getGPSData(buffer); + encoder.getGPSData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'A'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'B'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'C'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'D'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'E'); + + encoder.getGPSData(buffer); + encoder.getGPSData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'F'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'I'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'J'); + + encoder.getGPSData(buffer); + encoder.getGPSData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x4U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'K'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'L'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'M'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'N'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + for(unsigned int i = 18; i < 60U; i+= 6U) { + encoder.getGPSData(buffer); + encoder.getGPSData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } +} \ No newline at end of file From d4f7a62baf1932e1d133fdf39f7c2962383fbfca Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 11:06:06 +0100 Subject: [PATCH 47/59] #9 test 2 times to make sure internal pointer goes back to start --- Tests/SlowDataEncoder/GPSData.cpp | 70 ++++++++++++++++--------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/Tests/SlowDataEncoder/GPSData.cpp b/Tests/SlowDataEncoder/GPSData.cpp index 47b3185..4e46e07 100644 --- a/Tests/SlowDataEncoder/GPSData.cpp +++ b/Tests/SlowDataEncoder/GPSData.cpp @@ -31,45 +31,47 @@ namespace SlowDataEncoderTests { CSlowDataEncoder encoder; encoder.setGPSData("ABCDEFGHIJKLMN"); + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; - unsigned char buffer[6U]; - - encoder.getGPSData(buffer); - encoder.getGPSData(buffer + 3); - EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); - EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'A'); - EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'B'); - EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'C'); - EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'D'); - EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'E'); - - encoder.getGPSData(buffer); - encoder.getGPSData(buffer + 3); - EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); - EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'F'); - EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); - EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); - EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'I'); - EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'J'); + encoder.getGPSData(buffer); + encoder.getGPSData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'A'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'B'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'C'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'D'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'E'); - encoder.getGPSData(buffer); - encoder.getGPSData(buffer + 3); - EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x4U); - EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'K'); - EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'L'); - EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'M'); - EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'N'); - EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + encoder.getGPSData(buffer); + encoder.getGPSData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'F'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'I'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'J'); - for(unsigned int i = 18; i < 60U; i+= 6U) { encoder.getGPSData(buffer); encoder.getGPSData(buffer + 3); - EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); - EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); - EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); - EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); - EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x4U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'K'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'L'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'M'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'N'); EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + for(unsigned int i = 18; i < 60U; i+= 6U) { + encoder.getGPSData(buffer); + encoder.getGPSData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } } } -} \ No newline at end of file +} From 80ee8d40daefbbbfe5eb7daec01d2bac88585b0d Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 15:09:46 +0100 Subject: [PATCH 48/59] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe31074..c087b34 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ I Use [Git flow](https://danielkummer.github.io/git-flow-cheatsheet/) as my work - Code formating rules are observed (these are very lousy though) ## 4.2. Continuous Integration I have added some basic CI using CircleCI [![F4FXL](https://circleci.com/gh/F4FXL/DStarGateway.svg?style=svg)](https://app.circleci.com/pipelines/github/F4FXL/DStarGateway?filter=all) I am trying to rewrite the code so that it can be put into some Behavior Driven Development scheme. This is a long haul task and I'll try do do it on the go while changing/adding stuff. -the testing framwrok used is Google Test. +the testing framwework used is Google Test. # 5. Version History ## 5.1. Version 0.5 From 7602c29f48e06b9fb46aaead9fae5907f5d40df1 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 17:21:57 +0100 Subject: [PATCH 49/59] #9 Add test for text data --- Tests/SlowDataEncoder/GPSData.cpp | 1 + Tests/SlowDataEncoder/TextData.cpp | 87 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 Tests/SlowDataEncoder/TextData.cpp diff --git a/Tests/SlowDataEncoder/GPSData.cpp b/Tests/SlowDataEncoder/GPSData.cpp index 4e46e07..21bc48f 100644 --- a/Tests/SlowDataEncoder/GPSData.cpp +++ b/Tests/SlowDataEncoder/GPSData.cpp @@ -62,6 +62,7 @@ namespace SlowDataEncoderTests EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'N'); EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + // all the remaining data shall be filled with 'f' for(unsigned int i = 18; i < 60U; i+= 6U) { encoder.getGPSData(buffer); encoder.getGPSData(buffer + 3); diff --git a/Tests/SlowDataEncoder/TextData.cpp b/Tests/SlowDataEncoder/TextData.cpp new file mode 100644 index 0000000..b2ba775 --- /dev/null +++ b/Tests/SlowDataEncoder/TextData.cpp @@ -0,0 +1,87 @@ +/* + * 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 + +#include "../../SlowDataEncoder.h" +#include "../../DStarDefines.h" + +namespace SlowDataEncoderTests +{ + class SlowDataEncoder_textData : public ::testing::Test { + + }; + + TEST_F(SlowDataEncoder_textData, textDataCorrectlySet) + { + CSlowDataEncoder encoder; + encoder.setTextData("ABCDEFG"); + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; + + encoder.getTextData(buffer); + encoder.getTextData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x0U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'A'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'B'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'C'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'D'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'E'); + + encoder.getTextData(buffer); + encoder.getTextData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x1U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'F'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getTextData(buffer); + encoder.getTextData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x2U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getTextData(buffer); + encoder.getTextData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x3U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + // all the remaining data shall be filled with 'f' + for(unsigned int i = 24; i < 60U; i+= 6U) { + encoder.getTextData(buffer); + encoder.getTextData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } + } +} From 4b4cea612f739389049ed37295337c81bef71d8c Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 17:58:40 +0100 Subject: [PATCH 50/59] #9 add test for header data --- Tests/SlowDataEncoder/HeaderData.cpp | 139 +++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Tests/SlowDataEncoder/HeaderData.cpp diff --git a/Tests/SlowDataEncoder/HeaderData.cpp b/Tests/SlowDataEncoder/HeaderData.cpp new file mode 100644 index 0000000..03d8204 --- /dev/null +++ b/Tests/SlowDataEncoder/HeaderData.cpp @@ -0,0 +1,139 @@ +/* + * 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 + +#include "../../SlowDataEncoder.h" +#include "../../DStarDefines.h" + +namespace SlowDataEncoderTests +{ + class SlowDataEncoder_headerData : public ::testing::Test { + + }; + + TEST_F(SlowDataEncoder_headerData, textDataCorrectlySet) + { + CHeaderData header; + header.setFlags('1', '2', '3'); + header.setMyCall1("F4FXL"); + header.setMyCall2("5100"); + header.setYourCall("CQCQCQ"); + header.setRptCall1("F5ZEE B"); + header.setRptCall2("F5ZEE G"); + + CSlowDataEncoder encoder; + encoder.setHeaderData(header); + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, '1'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, '2'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, '3'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'F'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, '5'); + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'Z'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'E'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'E'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'F'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, '5'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'Z'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'E'); + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'E'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'B'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'C'); + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'Q'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'C'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'Q'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'C'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'Q'); + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'F'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, '4'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'F'); + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'X'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'L'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, '5'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, '1'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, '0'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, '0'); + EXPECT_NE(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); // we do not check the actual result of the CRC, we just make sure is inot 'f' + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x1U); + EXPECT_NE(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); // done with crc check, remaining shall be filled with 'f' + + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + encoder.getHeaderData(buffer); + encoder.getHeaderData(buffer + 3); + // all the remaining data shall be filled with 'f' + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } +} From 30e48938d51abc8d05f68f327e58d9f0fde48ecd Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 18:29:07 +0100 Subject: [PATCH 51/59] #9 fix test name --- Tests/SlowDataEncoder/HeaderData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/SlowDataEncoder/HeaderData.cpp b/Tests/SlowDataEncoder/HeaderData.cpp index 03d8204..a3d253c 100644 --- a/Tests/SlowDataEncoder/HeaderData.cpp +++ b/Tests/SlowDataEncoder/HeaderData.cpp @@ -27,7 +27,7 @@ namespace SlowDataEncoderTests }; - TEST_F(SlowDataEncoder_headerData, textDataCorrectlySet) + TEST_F(SlowDataEncoder_headerData, headerDataCorrectlySet) { CHeaderData header; header.setFlags('1', '2', '3'); From eee1c223bc81693508bb97cd5e85178192cee433 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 18:29:31 +0100 Subject: [PATCH 52/59] #9 add interleave data tests, fix lenght calculation --- SlowDataEncoder.cpp | 2 +- Tests/SlowDataEncoder/InterleavedData.cpp | 354 ++++++++++++++++++++++ 2 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 Tests/SlowDataEncoder/InterleavedData.cpp diff --git a/SlowDataEncoder.cpp b/SlowDataEncoder.cpp index 56442e3..a1aa75f 100644 --- a/SlowDataEncoder.cpp +++ b/SlowDataEncoder.cpp @@ -190,7 +190,7 @@ unsigned int CSlowDataEncoder::getInterleavedDataLength() m_interleavedDataFullSize = 0U; if(m_textData) m_interleavedDataFullSize += TEXT_SIZE; if(m_headerData) m_interleavedDataFullSize += SLOW_DATA_BLOCK_SIZE; - if(m_gpsData) m_interleavedDataFullSize += m_gpsDataFullSize; + if(m_gpsData) m_interleavedDataFullSize += m_gpsDataSize; m_interleavedDataFullSize = roundUpToMultipleOf(m_interleavedDataFullSize, SLOW_DATA_FULL_BLOCK_SIZE); //SLOW_DATA_FULL_BLOCK_SIZE * (1U + ((m_interleavedDataFullSize - 1U) / SLOW_DATA_FULL_BLOCK_SIZE)); return m_interleavedDataFullSize; } diff --git a/Tests/SlowDataEncoder/InterleavedData.cpp b/Tests/SlowDataEncoder/InterleavedData.cpp new file mode 100644 index 0000000..a9c79be --- /dev/null +++ b/Tests/SlowDataEncoder/InterleavedData.cpp @@ -0,0 +1,354 @@ +/* + * 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 + +#include "../../SlowDataEncoder.h" +#include "../../DStarDefines.h" + +namespace SlowDataEncoderTests +{ + class SlowDataEncoder_interleavedData : public ::testing::Test { + + }; + + TEST_F(SlowDataEncoder_interleavedData, gpsAndTextData) + { + //here we only test for correct interleaving + CSlowDataEncoder encoder; + encoder.setTextData("TTTTTT"); // 6 times T + encoder.setGPSData("GGGGGGGGGGGGGGGGGGGGGG"); // 22 times G + + auto dataLen = encoder.getInterleavedDataLength(); + + EXPECT_EQ(dataLen, 60);// including data type bytes we need 54 (20 + 5 + 22 + 6) bytes, this shall be rounded up to next block size multiple, in this case 60 + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x0U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'T'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'T'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'T'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x1U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x2U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x3U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x2U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + //remaining shall only be 'f" + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } + + TEST_F(SlowDataEncoder_interleavedData, onlyGPSData) + { + CSlowDataEncoder encoder; + encoder.setGPSData("ABCDEFGHIJKLMN"); + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'A'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'B'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'C'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'D'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'E'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'F'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'I'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'J'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x4U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'K'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'L'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'M'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'N'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + // all the remaining data shall be filled with 'f' + for(unsigned int i = 18; i < 60U; i+= 6U) { + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } + } + + TEST_F(SlowDataEncoder_interleavedData, onlyTextData) + { + CSlowDataEncoder encoder; + encoder.setTextData("ABCDEFG"); + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x0U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'A'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'B'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'C'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'D'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'E'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x1U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'F'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x2U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x3U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + // all the remaining data shall be filled with 'f' + for(unsigned int i = 24; i < 60U; i+= 6U) { + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } + } + + TEST_F(SlowDataEncoder_interleavedData, onlyHeaderData) + { + CHeaderData header; + header.setFlags('1', '2', '3'); + header.setMyCall1("F4FXL"); + header.setMyCall2("5100"); + header.setYourCall("CQCQCQ"); + header.setRptCall1("F5ZEE B"); + header.setRptCall2("F5ZEE G"); + + CSlowDataEncoder encoder; + encoder.setHeaderData(header); + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, '1'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, '2'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, '3'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'F'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, '5'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'Z'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'E'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'E'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'F'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, '5'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'Z'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'E'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'E'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'B'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'C'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'Q'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'C'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'Q'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'C'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'Q'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'F'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, '4'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'F'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'X'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'L'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, '5'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, '1'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, '0'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, '0'); + EXPECT_NE(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); // we do not check the actual result of the CRC, we just make sure is inot 'f' + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x1U); + EXPECT_NE(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); // done with crc check, remaining shall be filled with 'f' + + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + // all the remaining data shall be filled with 'f' + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } +} From 5fc9a90156d4de536a9a1397e11ed9169afcbce0 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 20:05:51 +0100 Subject: [PATCH 53/59] #9 fix interleved data length and add more tests --- SlowDataEncoder.cpp | 4 +- Tests/SlowDataEncoder/InterleavedData.cpp | 511 ++++++++++++++++++++++ 2 files changed, 513 insertions(+), 2 deletions(-) diff --git a/SlowDataEncoder.cpp b/SlowDataEncoder.cpp index a1aa75f..f519ee6 100644 --- a/SlowDataEncoder.cpp +++ b/SlowDataEncoder.cpp @@ -168,7 +168,7 @@ void CSlowDataEncoder::buildInterleavedData() if(m_headerData != nullptr) { //append header dat in one block at the end - ::memcpy(m_interleavedData + interleavedPtr, m_headerData, HEADER_SIZE); + ::memcpy(m_interleavedData + roundUpToMultipleOf(interleavedPtr, SLOW_DATA_FULL_BLOCK_SIZE), m_headerData, HEADER_SIZE); } } else if(m_textData != nullptr && m_gpsData == nullptr && m_headerData != nullptr){ @@ -189,7 +189,7 @@ unsigned int CSlowDataEncoder::getInterleavedDataLength() //calculate size (including filler bytes); m_interleavedDataFullSize = 0U; if(m_textData) m_interleavedDataFullSize += TEXT_SIZE; - if(m_headerData) m_interleavedDataFullSize += SLOW_DATA_BLOCK_SIZE; + if(m_headerData) m_interleavedDataFullSize += SLOW_DATA_FULL_BLOCK_SIZE; // the is because header data is transmitted as one contiguous block. Unused bytes fileld with 'f' if(m_gpsData) m_interleavedDataFullSize += m_gpsDataSize; m_interleavedDataFullSize = roundUpToMultipleOf(m_interleavedDataFullSize, SLOW_DATA_FULL_BLOCK_SIZE); //SLOW_DATA_FULL_BLOCK_SIZE * (1U + ((m_interleavedDataFullSize - 1U) / SLOW_DATA_FULL_BLOCK_SIZE)); return m_interleavedDataFullSize; diff --git a/Tests/SlowDataEncoder/InterleavedData.cpp b/Tests/SlowDataEncoder/InterleavedData.cpp index a9c79be..0651b15 100644 --- a/Tests/SlowDataEncoder/InterleavedData.cpp +++ b/Tests/SlowDataEncoder/InterleavedData.cpp @@ -17,6 +17,7 @@ */ #include +#include #include "../../SlowDataEncoder.h" #include "../../DStarDefines.h" @@ -27,6 +28,510 @@ namespace SlowDataEncoderTests }; + TEST_F(SlowDataEncoder_interleavedData, gpsAndHeaderData) + { + // Header is never interleaved, text and header are sent as two blocks + CHeaderData header; + unsigned char headerData[RADIO_HEADER_LENGTH_BYTES]; + ::memset(headerData, 'H', RADIO_HEADER_LENGTH_BYTES); + header.setData(headerData, RADIO_HEADER_LENGTH_BYTES, false); + + //here we only test for correct interleaving + CSlowDataEncoder encoder; + encoder.setGPSData("GGGGGGGG"); // 8 times G + encoder.setHeaderData(header); + + for(unsigned int i = 0U; i < 2U; i++) { + unsigned char buffer[6U]; + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x3U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + // all the remaining data shall be filled with 'f' until next block + for(unsigned int j = 12; j < 60U; j+= 6U) { + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_NE(buffer[5] ^ SCRAMBLER_BYTE3, 'f');// this is checksum byte, just make sure is is not 'f' + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x1U); + EXPECT_NE(buffer[1] ^ SCRAMBLER_BYTE2, 'f');// this is checksum byte, just make sure is is not 'f' + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + //remaining data is only 'f' + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } + + TEST_F(SlowDataEncoder_interleavedData, textAndHeaderData) + { + // Header is never interleaved, text and header are sent as two blocks + CHeaderData header; + unsigned char headerData[RADIO_HEADER_LENGTH_BYTES]; + ::memset(headerData, 'H', RADIO_HEADER_LENGTH_BYTES); + header.setData(headerData, RADIO_HEADER_LENGTH_BYTES, false); + + //here we only test for correct interleaving + CSlowDataEncoder encoder; + encoder.setTextData("TTTTTT"); // 6 times T + encoder.setHeaderData(header); + + auto dataLen = encoder.getInterleavedDataLength(); + EXPECT_EQ(dataLen, 120); //2* 60 + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x0U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'T'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'T'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'T'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x1U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x2U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x3U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + // all the remaining data shall be filled with 'f' until next block + for(unsigned int j = 24; j < 60U; j+= 6U) { + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_NE(buffer[5] ^ SCRAMBLER_BYTE3, 'f');// this is checksum byte, just make sure is is not 'f' + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x1U); + EXPECT_NE(buffer[1] ^ SCRAMBLER_BYTE2, 'f');// this is checksum byte, just make sure is is not 'f' + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + //remaining data is only 'f' + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } + + TEST_F(SlowDataEncoder_interleavedData, gpsTextAndHeaderData) + { + CHeaderData header; + unsigned char headerData[RADIO_HEADER_LENGTH_BYTES]; + ::memset(headerData, 'H', RADIO_HEADER_LENGTH_BYTES); + header.setData(headerData, RADIO_HEADER_LENGTH_BYTES, false); + + //here we only test for correct interleaving + CSlowDataEncoder encoder; + encoder.setTextData("TTTTTT"); // 6 times T + encoder.setGPSData("GGGGGGGGGGGGGGGGGGGGGG"); // 22 times G + encoder.setHeaderData(header); + + auto dataLen = encoder.getInterleavedDataLength(); + + EXPECT_EQ(dataLen, 120);// including data type bytes we need 54 (20 + 5 + 22 + 6) bytes, this shall be rounded up to next block size multiple, in this case 60 + + for(unsigned int testCount = 0U; testCount < 2U; testCount++) { + unsigned char buffer[6U]; + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x0U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'T'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'T'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'T'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x1U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'T'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x2U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_TEXT | 0x3U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, ' '); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, ' '); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, ' '); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, ' '); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'G'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'G'); + + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_GPS | 0x2U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'G'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'G'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + // header is not interleaved, attached as one contiguous block, but it only starts at the nex block + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'H'); + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x5U); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'H'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'H'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'H'); + EXPECT_NE(buffer[5] ^ SCRAMBLER_BYTE3, 'f');// this is checksum byte, just make sure is is not 'f' + + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, SLOW_DATA_TYPE_HEADER | 0x1U); + EXPECT_NE(buffer[1] ^ SCRAMBLER_BYTE2, 'f');// this is checksum byte, just make sure is is not 'f' + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + + //remaining data is only 'f' + encoder.getInterleavedData(buffer); + encoder.getInterleavedData(buffer + 3); + EXPECT_EQ(buffer[0] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[1] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[2] ^ SCRAMBLER_BYTE3, 'f'); + EXPECT_EQ(buffer[3] ^ SCRAMBLER_BYTE1, 'f'); + EXPECT_EQ(buffer[4] ^ SCRAMBLER_BYTE2, 'f'); + EXPECT_EQ(buffer[5] ^ SCRAMBLER_BYTE3, 'f'); + } + } + TEST_F(SlowDataEncoder_interleavedData, gpsAndTextData) { //here we only test for correct interleaving @@ -139,6 +644,9 @@ namespace SlowDataEncoderTests { CSlowDataEncoder encoder; encoder.setGPSData("ABCDEFGHIJKLMN"); + + auto dataLen = encoder.getInterleavedDataLength(); + EXPECT_EQ(dataLen, 60); for(unsigned int testCount = 0U; testCount < 2U; testCount++) { unsigned char buffer[6U]; @@ -188,6 +696,9 @@ namespace SlowDataEncoderTests { CSlowDataEncoder encoder; encoder.setTextData("ABCDEFG"); + + auto dataLen = encoder.getInterleavedDataLength(); + EXPECT_EQ(dataLen, 60); for(unsigned int testCount = 0U; testCount < 2U; testCount++) { unsigned char buffer[6U]; From 9d7c1ade08d63f31f22b722c745c5f7215dbb073 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 21:03:14 +0100 Subject: [PATCH 54/59] #9 clean up log debug --- GPSACollector.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/GPSACollector.cpp b/GPSACollector.cpp index ccbe4a6..3774713 100644 --- a/GPSACollector.cpp +++ b/GPSACollector.cpp @@ -37,7 +37,6 @@ CSentenceCollector(SLOW_DATA_TYPE_GPS, "$$CRC", '\x0D') bool CGPSACollector::isValidSentence(const std::string& sentence) { - CLog::logDebug("GPSA"); return isValidGPSA(sentence); } From 30d74f1f10434d30465109badaa8717f698cb4e6 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 13 Jan 2022 21:05:04 +0100 Subject: [PATCH 55/59] #9 set text for message, improve tests --- .vscode/tasks.json | 10 +++++----- APRSUnit.cpp | 8 ++++---- APRSUnit.h | 1 - APRStoDPRS.cpp | 11 ++++++++--- APRStoDPRS.h | 5 +++-- Tests/APRStoDPRS/aprsToDPRS.cpp | 24 ++++++++++++++++++------ 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ac3f100..168ca95 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,7 +12,10 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] }, { @@ -25,10 +28,7 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] } ] diff --git a/APRSUnit.cpp b/APRSUnit.cpp index 49e7f4b..3fe066b 100644 --- a/APRSUnit.cpp +++ b/APRSUnit.cpp @@ -33,7 +33,6 @@ m_out(0U), m_seq(0U), m_totalNeeded(0U), m_timer(1000U, 2U), -m_dprs(), m_start() { m_timer.start(); @@ -57,13 +56,14 @@ void CAPRSUnit::clock(unsigned int ms) m_frameBuffer.pop_front(); m_headerData = new CHeaderData(); - CAPRSToDPRS::aprsToDPRS(m_dprs, *m_headerData, *frame); + std::string dprs, text; + CAPRSToDPRS::aprsToDPRS(dprs, text, *m_headerData, *frame); m_slowData = new CSlowDataEncoder(); m_slowData->setHeaderData(*m_headerData); - m_slowData->setGPSData(m_dprs); - m_slowData->setTextData("APRS to DPRS"); + m_slowData->setGPSData(dprs); + m_slowData->setTextData(text); m_totalNeeded = (m_slowData->getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U; diff --git a/APRSUnit.h b/APRSUnit.h index 466f6d5..bc7a595 100644 --- a/APRSUnit.h +++ b/APRSUnit.h @@ -51,7 +51,6 @@ private: unsigned int m_seq; unsigned int m_totalNeeded; CTimer m_timer; - std::string m_dprs; std::chrono::high_resolution_clock::time_point m_start; }; diff --git a/APRStoDPRS.cpp b/APRStoDPRS.cpp index 8b524a1..d238a2f 100644 --- a/APRStoDPRS.cpp +++ b/APRStoDPRS.cpp @@ -22,13 +22,16 @@ #include "Log.h" #include "RSMS1AMessageBuilder.h" -bool CAPRSToDPRS::aprsToDPRS(std::string& dprs, CHeaderData& header, CAPRSFrame& frame) + + +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, header, frame); + return messageToDPRS(dprs, text, header, frame); default: break; } @@ -36,7 +39,7 @@ bool CAPRSToDPRS::aprsToDPRS(std::string& dprs, CHeaderData& header, CAPRSFrame& return false; } -bool CAPRSToDPRS::messageToDPRS(std::string& dprs, CHeaderData& header, CAPRSFrame& frame) +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] != ':') { @@ -64,5 +67,7 @@ bool CAPRSToDPRS::messageToDPRS(std::string& dprs, CHeaderData& header, CAPRSFra CRSMS1AMessageBuilder::buildMessage(dprs, frame.getSource(), recipient, messageBody); + text = messageBody; + return true; } \ No newline at end of file diff --git a/APRStoDPRS.h b/APRStoDPRS.h index 2e185d7..05191c0 100644 --- a/APRStoDPRS.h +++ b/APRStoDPRS.h @@ -19,6 +19,7 @@ #pragma once #include +#include #include "HeaderData.h" #include "APRSFrame.h" @@ -26,8 +27,8 @@ class CAPRSToDPRS { public: - static bool aprsToDPRS(std::string& dprs, CHeaderData& header, CAPRSFrame& frame); + static bool aprsToDPRS(std::string& dprs, std::string& text, CHeaderData& header, CAPRSFrame& frame); private: - static bool messageToDPRS(std::string& drps, CHeaderData& header, CAPRSFrame& frame); + static bool messageToDPRS(std::string& dprs, std::string& text, CHeaderData& header, CAPRSFrame& frame); }; \ No newline at end of file diff --git a/Tests/APRStoDPRS/aprsToDPRS.cpp b/Tests/APRStoDPRS/aprsToDPRS.cpp index 94f8b29..872d3a0 100644 --- a/Tests/APRStoDPRS/aprsToDPRS.cpp +++ b/Tests/APRStoDPRS/aprsToDPRS.cpp @@ -30,35 +30,47 @@ namespace APRStoDPRSTests { CAPRSFrame frame("KC3FRA", "APRS", {"WIDE1-1", "WIDE2-2"}, ":F4FXL :Salut, comment vas tu?", APFT_MESSAGE); - std::string dprs; + std::string dprs, text; CHeaderData header; - bool ret = CAPRSToDPRS::aprsToDPRS(dprs, header, frame); + bool ret = CAPRSToDPRS::aprsToDPRS(dprs, text, header, frame); EXPECT_TRUE(ret); EXPECT_STREQ(dprs.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\r"); + EXPECT_STREQ(text.c_str(), "Salut, comment vas tu?"); + EXPECT_STREQ(header.getMyCall1().c_str(), "KC3FRA "); + EXPECT_STREQ(header.getMyCall2().c_str(), "MSG "); + EXPECT_STREQ(header.getYourCall().c_str(), "F4FXL "); } TEST_F(APRStoDPRS_aprsToDPRS, validMessageRecipientWithSSID) { CAPRSFrame frame("KC3FRA", "APRS", {"WIDE1-1", "WIDE2-2"}, ":F4FXL-7 :Salut, comment vas tu?", APFT_MESSAGE); - std::string dprs; + std::string dprs, text; CHeaderData header; - bool ret = CAPRSToDPRS::aprsToDPRS(dprs, header, frame); + bool ret = CAPRSToDPRS::aprsToDPRS(dprs, text, header, frame); EXPECT_TRUE(ret); EXPECT_STREQ(dprs.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\r"); + EXPECT_STREQ(text.c_str(), "Salut, comment vas tu?"); + EXPECT_STREQ(header.getMyCall1().c_str(), "KC3FRA "); + EXPECT_STREQ(header.getMyCall2().c_str(), "MSG "); + EXPECT_STREQ(header.getYourCall().c_str(), "F4FXL "); } TEST_F(APRStoDPRS_aprsToDPRS, emptyRecipient) { CAPRSFrame frame("KC3FRA", "APRS", {"WIDE1-1", "WIDE2-2"}, ": :Salut, comment vas tu?", APFT_MESSAGE); - std::string dprs; + std::string dprs, text; CHeaderData header; - bool ret = CAPRSToDPRS::aprsToDPRS(dprs, header, frame); + bool ret = CAPRSToDPRS::aprsToDPRS(dprs, text, header, frame); EXPECT_FALSE(ret); EXPECT_STREQ(dprs.c_str(), ""); + EXPECT_STREQ(text.c_str(), ""); + EXPECT_STREQ(header.getMyCall1().c_str(), " "); + EXPECT_STREQ(header.getMyCall2().c_str(), " "); + EXPECT_STREQ(header.getYourCall().c_str(), " "); } } From 0db54e5026e629cbbdddebe938c92d8b1c9e93bf Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 14 Jan 2022 17:20:00 +0100 Subject: [PATCH 56/59] #9 add telemetry parsing --- .vscode/tasks.json | 10 +- APRSFrame.h | 3 +- APRSParser.cpp | 21 ++- Tests/APRSParser/parseAPRSFrame.cpp | 276 ++++++++++++++++++---------- 4 files changed, 204 insertions(+), 106 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 168ca95..ac3f100 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,10 +12,7 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] }, { @@ -28,7 +25,10 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] } ] diff --git a/APRSFrame.h b/APRSFrame.h index 2fc790c..ca6ec85 100644 --- a/APRSFrame.h +++ b/APRSFrame.h @@ -29,7 +29,8 @@ enum APRS_FRAME_TYPE { APFT_NMEA, APFT_STATUS, APFT_OBJECT, - APFT_WX + APFT_WX, + APFT_TELEMETRY }; class CAPRSFrame { diff --git a/APRSParser.cpp b/APRSParser.cpp index cdd7b5a..0eeaabd 100644 --- a/APRSParser.cpp +++ b/APRSParser.cpp @@ -106,9 +106,21 @@ bool CAPRSParser::parseInt(CAPRSFrame& frame) } break; case ':': - if(body[9] == ':' && std::all_of(body.begin(), body.begin() + 9, - [](char c){ return c == ' ' || c == '-' || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); })) + // 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; @@ -121,6 +133,11 @@ bool CAPRSParser::parseInt(CAPRSFrame& frame) case '{': type = APFT_UNKNOWN; // break; + case 'T': + if(body[0] == '#') { + type = APFT_TELEMETRY; + } + break; default: type = APFT_UNKNOWN; break; diff --git a/Tests/APRSParser/parseAPRSFrame.cpp b/Tests/APRSParser/parseAPRSFrame.cpp index 0263348..fdead96 100644 --- a/Tests/APRSParser/parseAPRSFrame.cpp +++ b/Tests/APRSParser/parseAPRSFrame.cpp @@ -20,102 +20,182 @@ #include "../../APRSParser.h" -class APRSParser_parseAPRSFrame : public ::testing::Test { - -}; - -TEST_F(APRSParser_parseAPRSFrame, EmpyString) { - - CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("", aprsFrame); - - EXPECT_FALSE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ""); - EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), ""); - EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); - EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); - EXPECT_EQ(aprsFrame.getPath().size(), 0U); -} - -TEST_F(APRSParser_parseAPRSFrame, NoSourceCallsign) { - - CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame(">APRS::F4ABC Test Message", aprsFrame); - - EXPECT_FALSE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ""); - EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), ""); - EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); - EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); - EXPECT_EQ(aprsFrame.getPath().size(), 0U); +namespace APRSParserTests +{ + class APRSParser_parseAPRSFrame : public ::testing::Test { + + }; + + TEST_F(APRSParser_parseAPRSFrame, EmpyString) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("", aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ""); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), ""); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); + EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); + EXPECT_EQ(aprsFrame.getPath().size(), 0U); + } + + TEST_F(APRSParser_parseAPRSFrame, NoSourceCallsign) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame(">APRS::F4ABC Test Message", aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ""); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), ""); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); + EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); + EXPECT_EQ(aprsFrame.getPath().size(), 0U); + } + + TEST_F(APRSParser_parseAPRSFrame, NoDestCallsign) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("N0CALL>::F4ABC Test Message", aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ""); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), ""); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); + EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); + EXPECT_EQ(aprsFrame.getPath().size(), 0U); + } + + TEST_F(APRSParser_parseAPRSFrame, CorrectMessageFrameWithDigipeater) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("N0CALL>APRS,WIDE1-1,WIDE2-2::F4ABC :Test Message", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F4ABC :Test Message"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "N0CALL"); + EXPECT_EQ(aprsFrame.getType(), APFT_MESSAGE); + EXPECT_EQ(aprsFrame.getPath().size(), 2); + EXPECT_STREQ(aprsFrame.getPath()[0].c_str(), "WIDE1-1"); + EXPECT_STREQ(aprsFrame.getPath()[1].c_str(), "WIDE2-2"); + } + + TEST_F(APRSParser_parseAPRSFrame, CorrectMessageFrameWithoutDigipeater) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("N0CALL>APRS::F4ABC :Test Message", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F4ABC :Test Message"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "N0CALL"); + EXPECT_EQ(aprsFrame.getType(), APFT_MESSAGE); + EXPECT_EQ(aprsFrame.getPath().size(), 0); + } + + TEST_F(APRSParser_parseAPRSFrame, InvalidMessageFrame) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("N0CALL>APRS::F4ABC&@#$:Test Message", aprsFrame); + + EXPECT_FALSE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ""); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), ""); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); + EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); + EXPECT_EQ(aprsFrame.getPath().size(), 0U); + } + + TEST_F(APRSParser_parseAPRSFrame, ID51) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("F4FXL-8>API51,DSTAR:!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "API51"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F4FXL-8"); + EXPECT_EQ(aprsFrame.getType(), APFT_POSITION); + EXPECT_EQ(aprsFrame.getPath().size(), 1); + EXPECT_STREQ(aprsFrame.getPath()[0].c_str(), "DSTAR"); + } + + TEST_F(APRSParser_parseAPRSFrame, telemetryLabels) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("F5ZEE-C>APRS::F5ZEE-C :PARM.PA Temp", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F5ZEE-C :PARM.PA Temp"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F5ZEE-C"); + EXPECT_EQ(aprsFrame.getType(), APFT_TELEMETRY); + EXPECT_EQ(aprsFrame.getPath().size(), 0); + } + + TEST_F(APRSParser_parseAPRSFrame, telemetryEQNS) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("F5ZEE-C>APRS::F5ZEE-C :EQNS.0,0.16016,-40,0,0,0,0,0,0,0,0,0,0,0,0", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F5ZEE-C :EQNS.0,0.16016,-40,0,0,0,0,0,0,0,0,0,0,0,0"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F5ZEE-C"); + EXPECT_EQ(aprsFrame.getType(), APFT_TELEMETRY); + EXPECT_EQ(aprsFrame.getPath().size(), 0); + } + + TEST_F(APRSParser_parseAPRSFrame, telemetryEQNSWithSeqnum) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("F5ZEE-C>APRS::F5ZEE-C :EQNS.0,0.16016,-40,0,0,0,0,0,0,0,0,0,0,0,0{ABCD", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F5ZEE-C :EQNS.0,0.16016,-40,0,0,0,0,0,0,0,0,0,0,0,0{ABCD"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F5ZEE-C"); + EXPECT_EQ(aprsFrame.getType(), APFT_MESSAGE); + EXPECT_EQ(aprsFrame.getPath().size(), 0); + } + + + TEST_F(APRSParser_parseAPRSFrame, telemetryReport) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("F5ZEE-C>APRS:T#581,342,000,000,000,000,00000000", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "T#581,342,000,000,000,000,00000000"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F5ZEE-C"); + EXPECT_EQ(aprsFrame.getType(), APFT_TELEMETRY); + EXPECT_EQ(aprsFrame.getPath().size(), 0); + } + + TEST_F(APRSParser_parseAPRSFrame, messageToSelf) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("F4ABC>APRS::F4ABC :Test Message{ABCD", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F4ABC :Test Message{ABCD"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F4ABC"); + EXPECT_EQ(aprsFrame.getType(), APFT_MESSAGE); + EXPECT_EQ(aprsFrame.getPath().size(), 0); + } + + TEST_F(APRSParser_parseAPRSFrame, messageToSelfNoSeqNum) + { + CAPRSFrame aprsFrame; + bool retVal = CAPRSParser::parseFrame("F4ABC>APRS::F4ABC :Test Message", aprsFrame); + + EXPECT_TRUE(retVal); + EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F4ABC :Test Message"); + EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); + EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F4ABC"); + EXPECT_EQ(aprsFrame.getType(), APFT_MESSAGE); + EXPECT_EQ(aprsFrame.getPath().size(), 0); + } } - -TEST_F(APRSParser_parseAPRSFrame, NoDestCallsign) { - - CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("N0CALL>::F4ABC Test Message", aprsFrame); - - EXPECT_FALSE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ""); - EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), ""); - EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); - EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); - EXPECT_EQ(aprsFrame.getPath().size(), 0U); -} - -TEST_F(APRSParser_parseAPRSFrame, CorrectMessageFrameWithDigipeater) { - - CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("N0CALL>APRS,WIDE1-1,WIDE2-2::F4ABC :Test Message", aprsFrame); - - EXPECT_TRUE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F4ABC :Test Message"); - EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); - EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "N0CALL"); - EXPECT_EQ(aprsFrame.getType(), APFT_MESSAGE); - EXPECT_EQ(aprsFrame.getPath().size(), 2); - EXPECT_STREQ(aprsFrame.getPath()[0].c_str(), "WIDE1-1"); - EXPECT_STREQ(aprsFrame.getPath()[1].c_str(), "WIDE2-2"); -} - -TEST_F(APRSParser_parseAPRSFrame, CorrectMessageFrameWithoutDigipeater) { - - CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("N0CALL>APRS::F4ABC :Test Message", aprsFrame); - - EXPECT_TRUE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ":F4ABC :Test Message"); - EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "APRS"); - EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "N0CALL"); - EXPECT_EQ(aprsFrame.getType(), APFT_MESSAGE); - EXPECT_EQ(aprsFrame.getPath().size(), 0); -} - -TEST_F(APRSParser_parseAPRSFrame, InvalidMessageFrame) { - - CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("N0CALL>APRS::F4ABC&@#$:Test Message", aprsFrame); - - EXPECT_FALSE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), ""); - EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), ""); - EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), ""); - EXPECT_EQ(aprsFrame.getType(), APFT_UNKNOWN); - EXPECT_EQ(aprsFrame.getPath().size(), 0U); -} - -// - -TEST_F(APRSParser_parseAPRSFrame, ID51) { - - CAPRSFrame aprsFrame; - bool retVal = CAPRSParser::parseFrame("F4FXL-8>API51,DSTAR:!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n", aprsFrame); - - EXPECT_TRUE(retVal); - EXPECT_STRCASEEQ(aprsFrame.getBody().c_str(), "!1234.56N/12345.67E[/A=000886QRV DStar\r\r\n"); - EXPECT_STRCASEEQ(aprsFrame.getDestination().c_str(), "API51"); - EXPECT_STRCASEEQ(aprsFrame.getSource().c_str(), "F4FXL-8"); - EXPECT_EQ(aprsFrame.getType(), APFT_POSITION); - EXPECT_EQ(aprsFrame.getPath().size(), 1); - EXPECT_STREQ(aprsFrame.getPath()[0].c_str(), "DSTAR"); -} \ No newline at end of file From a373488b6abbb95904a9ba0466e474d8732693a2 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 14 Jan 2022 17:50:14 +0100 Subject: [PATCH 57/59] #9 update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c087b34..7cb4184 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ the testing framwework used is Google Test. # 5. Version History ## 5.1. Version 0.5 +- [Improvement] Add forwarding of RS-MS1A messages to APRS-IS ([#9](https://github.com/F4FXL/DStarGateway/issues/9)) - [Bugfix] Failed to download XLX Hosts when URL contains a = sign ([#14](https://github.com/F4FXL/DStarGateway/issues/14)) - [Bugfix] Remote control connection failed ([#13](https://github.com/F4FXL/DStarGateway/issues/13)) - [Bugfix] Trying to connect to ghost ircDDB when no ircDDB is configured From 17268c243fa5b19bc2c011d5835b6a973ef2fd20 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 14 Jan 2022 17:55:53 +0100 Subject: [PATCH 58/59] #9 update readem --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7cb4184..9b75943 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ - [1. Introduction](#1-introduction) - [2. Current State](#2-current-state) - [2.1. Code sanity](#21-code-sanity) - - [2.2. Code Credit](#22-code-credit) - - [2.3. Features](#23-features) + - [2.2. Code Credits](#22-code-credits) + - [2.3. Thanks](#23-thanks) + - [2.4. Features](#24-features) - [3. Building and installing](#3-building-and-installing) - [3.1. Initial setup](#31-initial-setup) - [3.2. Get latest stable version (recommended)](#32-get-latest-stable-version-recommended) @@ -34,11 +35,14 @@ The current code is working, yet ugly IMHO as it is a mix of C and C++ of variou The code has also been amended to no longer rely on compiler defines for paths like log or data. These can be set in configuration file. Quite a few classes are more or less copy/paste from each other some sanitization by using base classes or template classes would greatly improve code maintainibility. Maybe one day ;) -## 2.2. Code Credit +## 2.2. Code Credits - Jonathan Naylor G4KLX (The original author of [ircddbGateway](https://github.com/g4klx/ircDDBGateway)) - Thomas A. Early N7TAE (Code taken from his [smart-group](https://github.com/n7tae/smart-group-server) software) - Geoffrey Merck F4FXL / KC3FRA [That's me !](https://github.com/F4FXL/) -## 2.3. Features +## 2.3. Thanks +- Cyrille F1MHV / DF1CHB for the testing +- Jonathan Naylor G4KLX for all the work ahead +## 2.4. Features All the features found in ircddbGateway are supposed to be working. I have mixed feelings about putting these back in or not. Features that where left out : From 58e2e74c5b6587b20e66b851982ef4a8338acf0e Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 15 Jan 2022 17:44:42 +0100 Subject: [PATCH 59/59] #9 fix unsupported frames trigerring tx --- .vscode/tasks.json | 10 +++++----- APRSUnit.cpp | 9 +++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ac3f100..168ca95 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,7 +12,10 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] }, { @@ -25,10 +28,7 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] } ] diff --git a/APRSUnit.cpp b/APRSUnit.cpp index 3fe066b..ad8f9e1 100644 --- a/APRSUnit.cpp +++ b/APRSUnit.cpp @@ -51,13 +51,17 @@ void CAPRSUnit::clock(unsigned int ms) { m_timer.clock(ms); if(m_status == APS_IDLE && !m_frameBuffer.empty() && m_timer.hasExpired()) { - m_status = APS_TRANSMIT; + auto frame = m_frameBuffer.front(); m_frameBuffer.pop_front(); m_headerData = new CHeaderData(); std::string dprs, text; - CAPRSToDPRS::aprsToDPRS(dprs, text, *m_headerData, *frame); + if(!CAPRSToDPRS::aprsToDPRS(dprs, text, *m_headerData, *frame)) { + delete m_headerData; + m_headerData = nullptr; + return; + } m_slowData = new CSlowDataEncoder(); @@ -73,6 +77,7 @@ void CAPRSUnit::clock(unsigned int ms) m_seq = 0U; m_start = std::chrono::high_resolution_clock::now(); + m_status = APS_TRANSMIT; return; }