From da924acb7ca500b8667e3e6595985b18751b0481 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 9 Jan 2022 22:19:42 +0100 Subject: [PATCH] #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 @@ class CAPRSUnit 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(), ""); + } +}