/* * Copyright (C) 2010,2011,2012,2018 by Jonathan Naylor G4KLX * Copyright (C) 2021 by Geoffrey Merck F4FXL / KC3FRA * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include "StringUtils.h" #include "Log.h" #include "APRSWriter.h" #include "DStarDefines.h" #include "Defs.h" #include "Log.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), m_gateway(), m_address(), m_port(0U), m_array(), m_idFrameProvider(nullptr) { assert(!hostname.empty()); assert(port > 0U); assert(!gateway.empty()); assert(!password.empty()); m_thread = new CAPRSWriterThread(gateway, password, address, hostname, port); m_gateway = gateway; m_gateway = m_gateway.substr(0, LONG_CALLSIGN_LENGTH - 1U); boost::trim(m_gateway); } CAPRSWriter::~CAPRSWriter() { for(auto it = m_array.begin(); it != m_array.end(); it++) { delete it->second; } 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) { std::string temp = callsign; temp.resize(LONG_CALLSIGN_LENGTH - 1U, ' '); temp += band; m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, latitude, longitude, agl); } bool CAPRSWriter::open() { return m_thread->start(); } void CAPRSWriter::writeHeader(const std::string& callsign, const CHeaderData& header) { CAPRSEntry* entry = m_array[callsign]; if (entry == NULL) { CLog::logError("Cannot find the callsign \"%s\" in the APRS array", callsign.c_str()); return; } entry->reset(); CAPRSCollector* collector = entry->getCollector(); collector->writeHeader(header.getMyCall1()); } void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) { if (data.isEnd()) return; CAPRSEntry* entry = m_array[callsign]; if (entry == NULL) { CLog::logError("Cannot find the callsign \"%s\" in the APRS array", callsign.c_str()); return; } CAPRSCollector* collector = entry->getCollector(); if (data.isSync()) { collector->sync(); return; } unsigned char buffer[400U]; data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES); bool complete = collector->writeData(buffer + VOICE_FRAME_LENGTH_BYTES); if (!complete) return; if (!m_thread->isConnected()) { collector->reset(); return; } // Check the transmission timer bool ok = entry->isOK(); if (!ok) { collector->reset(); return; } unsigned int length = collector->getData(buffer, 400U); std::string text((char*)buffer, length); auto n = text.find(':'); if (n == std::string::npos) { collector->reset(); 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) 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()); char ascii[500U]; ::memset(ascii, 0x00, 500U); for (unsigned int i = 0U; i < output.length(); i++) ascii[i] = output[i]; m_thread->write(ascii); collector->reset(); } void CAPRSWriter::writeStatus(const std::string& callsign, const std::string status) { CAPRSEntry* entry = m_array[callsign]; if (entry == NULL) { CLog::logError("Cannot find the callsign \"%s\" in the APRS array", callsign.c_str()); return; } entry->getStatus().setStatus(status); } void CAPRSWriter::clock(unsigned int ms) { m_thread->clock(ms); if(m_idFrameProvider != nullptr) { m_idFrameProvider->clock(ms); if(m_idFrameProvider->wantsToSend()) { sendIdFrames(); } } for (auto it : m_array) { if(it.second != NULL) { it.second->clock(ms); if(it.second->getStatus().isOutOfDate()) sendStatusFrame(it.second); } } } void CAPRSWriter::sendStatusFrame(CAPRSEntry * entry) { assert(entry != nullptr); if(!m_thread->isConnected()) return; auto linkStatus = entry->getStatus(); std::string body = boost::trim_copy(linkStatus.getStatus()); if(body[0] != '>') body = '>' + body; 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()); } void CAPRSWriter::sendIdFrames() { if(m_thread->isConnected()) { for(auto entry : m_array) { std::vector frames; if(m_idFrameProvider->buildAPRSFrames(m_gateway, entry.second, frames)) { for(auto frame : frames) { m_thread->write(frame.c_str()); } } } } } bool CAPRSWriter::isConnected() const { return m_thread->isConnected(); } void CAPRSWriter::close() { if(m_idFrameProvider != nullptr) { m_idFrameProvider->close(); delete m_idFrameProvider; m_idFrameProvider = nullptr; } m_thread->stop(); }