diff --git a/APRSEntry.cpp b/APRSEntry.cpp index a9fbdcf..879b536 100644 --- a/APRSEntry.cpp +++ b/APRSEntry.cpp @@ -32,7 +32,8 @@ m_longitude(longitude), m_agl(agl), m_timer(1000U, 10U), m_first(true), -m_collector(NULL) +m_collector(NULL), +m_linkStatus() { boost::trim(m_callsign); @@ -89,6 +90,11 @@ CAPRSCollector* CAPRSEntry::getCollector() const return m_collector; } +CAPRSEntryLinkStatus& CAPRSEntry::getLinkStatus() +{ + return m_linkStatus; +} + void CAPRSEntry::reset() { m_first = true; @@ -98,6 +104,7 @@ void CAPRSEntry::reset() void CAPRSEntry::clock(unsigned int ms) { + m_linkStatus.clock(ms); m_timer.clock(ms); } diff --git a/APRSEntry.h b/APRSEntry.h index 3ad5484..53335e2 100644 --- a/APRSEntry.h +++ b/APRSEntry.h @@ -22,7 +22,9 @@ #include #include "APRSCollector.h" +#include "APRSEntryLinkStatus.h" #include "Timer.h" +#include "Defs.h" class CAPRSEntry { public: @@ -38,6 +40,7 @@ public: double getLongitude() const; double getAGL() const; CAPRSCollector* getCollector() const; + CAPRSEntryLinkStatus& getLinkStatus(); // Transmission timer void reset(); @@ -56,4 +59,5 @@ private: CTimer m_timer; bool m_first; CAPRSCollector* m_collector; + CAPRSEntryLinkStatus m_linkStatus; }; diff --git a/APRSEntryLinkStatus.cpp b/APRSEntryLinkStatus.cpp new file mode 100644 index 0000000..8a8b19d --- /dev/null +++ b/APRSEntryLinkStatus.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 "APRSEntryLinkStatus.h" + +CAPRSEntryLinkStatus::CAPRSEntryLinkStatus() : +m_linkStatus(LS_NONE), +m_linkDestination(), +m_linkStatusChanged(false), +m_timer(1000U) +{ + m_timer.setTimeout(20U * 60U); //statuses go out every 20 minutes or every change +} + +bool CAPRSEntryLinkStatus::isOutOfDate() +{ + return m_linkStatusChanged || m_timer.hasExpired(); +} + +std::string CAPRSEntryLinkStatus::getLinkDestination() const +{ + return m_linkDestination; +} + +LINK_STATUS CAPRSEntryLinkStatus::getLinkStatus() const +{ + return m_linkStatus; +} + +void CAPRSEntryLinkStatus::setLink(LINK_STATUS linkstatus, const std::string& destination) +{ + bool changed = m_linkStatus != linkstatus || m_linkDestination != destination; + if(changed) { + m_linkStatus = linkstatus; + m_linkDestination = destination; + } + m_linkStatusChanged = changed; +} + +void CAPRSEntryLinkStatus::reset() +{ + m_linkStatusChanged = false; + m_timer.start(); +} + +void CAPRSEntryLinkStatus::clock(unsigned int ms) +{ + m_timer.clock(ms); +} \ No newline at end of file diff --git a/APRSEntryLinkStatus.h b/APRSEntryLinkStatus.h new file mode 100644 index 0000000..c1834a3 --- /dev/null +++ b/APRSEntryLinkStatus.h @@ -0,0 +1,41 @@ +/* + * 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 "Timer.h" +#include "Defs.h" + +class CAPRSEntryLinkStatus +{ +public: + CAPRSEntryLinkStatus() ; + + LINK_STATUS getLinkStatus() const; + std::string getLinkDestination() const; + bool isOutOfDate(); + void setLink(LINK_STATUS linkstatus, const std::string& destination); + void reset(); + void clock(unsigned int ms); + +private: + LINK_STATUS m_linkStatus; + std::string m_linkDestination; + bool m_linkStatusChanged; + CTimer m_timer; +}; \ No newline at end of file diff --git a/APRSWriter.cpp b/APRSWriter.cpp index 513a151..2fdd71c 100644 --- a/APRSWriter.cpp +++ b/APRSWriter.cpp @@ -34,7 +34,8 @@ m_thread(NULL), m_gateway(), m_address(), m_port(0U), -m_array() +m_array(), +m_idFrameProvider(nullptr) { assert(!hostname.empty()); assert(port > 0U); @@ -157,6 +158,17 @@ void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) collector->reset(); } +void CAPRSWriter::writeLinkStatus(const std::string& callsign, LINK_STATUS status, const std::string& destination) +{ + CAPRSEntry* entry = m_array[callsign]; + if (entry == NULL) { + CLog::logError("Cannot find the callsign \"%s\" in the APRS array", callsign.c_str()); + return; + } + + entry->getLinkStatus().setLink(status, destination); +} + void CAPRSWriter::clock(unsigned int ms) { m_thread->clock(ms); @@ -168,12 +180,59 @@ void CAPRSWriter::clock(unsigned int ms) } } - for (auto it = m_array.begin(); it != m_array.end(); ++it) { - if(it->second != NULL) - it->second->clock(ms); + for (auto it : m_array) { + if(it.second != NULL) { + it.second->clock(ms); + if(it.second->getLinkStatus().isOutOfDate()) + sendStatusFrame(it.second); + } } } +void CAPRSWriter::sendStatusFrame(CAPRSEntry * entry) +{ + assert(entry != nullptr); + + // 20211231 TODO F4FXL support different languages + + if(!m_thread->isConnected()) + return; + + auto linkStatus = entry->getLinkStatus(); + std::string body; + + switch (linkStatus.getLinkStatus()) + { + case LS_LINKED_DCS: + case LS_LINKED_CCS: + case LS_LINKED_DEXTRA: + case LS_LINKED_DPLUS: + case LS_LINKED_LOOPBACK: + body = ">Linked to " + linkStatus.getLinkDestination(); + break; + + case LS_LINKING_DCS: + case LS_LINKING_CCS: + case LS_LINKING_DEXTRA: + case LS_LINKING_DPLUS: + case LS_LINKING_LOOPBACK: + body = ">Linking to " + linkStatus.getLinkDestination();; + break; + + default: + body = ">Not linked"; + break; + } + + 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()); + + m_thread->write(output.c_str()); + + linkStatus.reset(); +} + void CAPRSWriter::sendIdFrames() { if(m_thread->isConnected()) diff --git a/APRSWriter.h b/APRSWriter.h index f5b86d3..90f4156 100644 --- a/APRSWriter.h +++ b/APRSWriter.h @@ -51,6 +51,8 @@ public: void writeData(const std::string& callsign, const CAMBEData& data); + void writeLinkStatus(const std::string& callsign, LINK_STATUS status, const std::string& destination); + bool isConnected() const; void clock(unsigned int ms); @@ -66,6 +68,7 @@ private: CAPRSIdFrameProvider * m_idFrameProvider; void sendIdFrames(); + void sendStatusFrame(CAPRSEntry * entrry); }; #endif diff --git a/README.md b/README.md index f58ca15..7f9a104 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ I Use [Git flow](https://danielkummer.github.io/git-flow-cheatsheet/) as my work # 5. Version History ## 5.1. Version 0.4 +- [Improvement] Add APRS status link feature ([#8](https://github.com/F4FXL/DStarGateway/issues/8)) - [Bugfix] Posotions received over radio were not sent to APRS-IS when GPDS connection failed. ([#7](https://github.com/F4FXL/DStarGateway/issues/7)) - [Improvement] Bring back GPSD support (https://github.com/F4FXL/DStarGateway/issues/6) - [Improvement] Log enhancements ([#4])(https://github.com/F4FXL/DStarGateway/issues/4) diff --git a/RepeaterHandler.cpp b/RepeaterHandler.cpp index c64adb4..a5ea17c 100644 --- a/RepeaterHandler.cpp +++ b/RepeaterHandler.cpp @@ -1594,7 +1594,6 @@ void CRepeaterHandler::clockInt(unsigned int ms) m_starNet = NULL; break; #endif - case G2_ECHO: m_echo->end(); break; @@ -2564,6 +2563,9 @@ void CRepeaterHandler::writeLinkingTo(const std::string &callsign) m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); triggerInfo(); + if(m_aprsWriter != nullptr) + m_aprsWriter->writeLinkStatus(m_rptCallsign, m_linkStatus, callsign); + #ifdef USE_CCS m_ccsHandler->setReflector(); #endif @@ -2616,6 +2618,9 @@ void CRepeaterHandler::writeLinkedTo(const std::string &callsign) m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); triggerInfo(); + if(m_aprsWriter != nullptr) + m_aprsWriter->writeLinkStatus(m_rptCallsign, m_linkStatus, callsign); + #ifdef USE_CCS m_ccsHandler->setReflector(callsign); #endif @@ -2668,6 +2673,9 @@ void CRepeaterHandler::writeNotLinked() m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); triggerInfo(); + if(m_aprsWriter != nullptr) + m_aprsWriter->writeLinkStatus(m_rptCallsign, m_linkStatus, ""); + #ifdef USE_CCS m_ccsHandler->setReflector(); #endif @@ -2736,6 +2744,9 @@ void CRepeaterHandler::writeIsBusy(const std::string& callsign) m_infoAudio->setTempStatus(m_linkStatus, m_linkRepeater, tempText); triggerInfo(); + if(m_aprsWriter != nullptr) + m_aprsWriter->writeLinkStatus(m_rptCallsign, m_linkStatus, callsign); + #ifdef USE_CCS m_ccsHandler->setReflector(); #endif