diff --git a/APRSFixedIdFrameProvider.cpp b/APRSFixedIdFrameProvider.cpp new file mode 100644 index 0000000..80079fb --- /dev/null +++ b/APRSFixedIdFrameProvider.cpp @@ -0,0 +1,133 @@ +/* + * 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 +#include + +#include "APRSFixedIdFrameProvider.h" +#include "StringUtils.h" + +CAPRSFixedIdFrameProvider::CAPRSFixedIdFrameProvider() : +CAPRSIdFrameProvider(20U) // Initial timeout of 20 seconds +{ + +} + +bool CAPRSFixedIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector& frames) +{ + if (entry == nullptr) + return false; + + // Default values aren't passed on + if (entry->getLatitude() == 0.0 && entry->getLongitude() == 0.0) + return false; + + frames.clear(); + + time_t now; + ::time(&now); + struct tm* tm = ::gmtime(&now); + + std::string desc; + if (entry->getBand().length() > 1U) { + if (entry->getFrequency() != 0.0) + desc = CStringUtils::string_format("Data %.5lfMHz", entry->getFrequency()); + else + desc = "Data"; + } else { + if (entry->getFrequency() != 0.0) + desc = CStringUtils::string_format("Voice %.5lfMHz %c%.4lfMHz", + entry->getFrequency(), + entry->getOffset() < 0.0 ? '-' : '+', + std::fabs(entry->getOffset())); + else + desc = "Voice"; + } + + std::string band; + if (entry->getFrequency() >= 1200.0) + band = "1.2"; + else if (entry->getFrequency() >= 420.0) + band = "440"; + else if (entry->getFrequency() >= 144.0) + band = "2m"; + else if (entry->getFrequency() >= 50.0) + band = "6m"; + else if (entry->getFrequency() >= 28.0) + band = "10m"; + + double tempLat = ::fabs(entry->getLatitude()); + double tempLong = ::fabs(entry->getLongitude()); + + double latitude = ::floor(tempLat); + double longitude = ::floor(tempLong); + + latitude = (tempLat - latitude) * 60.0 + latitude * 100.0; + longitude = (tempLong - longitude) * 60.0 + longitude * 100.0; + + std::string lat; + if (latitude >= 1000.0F) + lat = CStringUtils::string_format("%.2lf", latitude); + else if (latitude >= 100.0F) + lat = CStringUtils::string_format("0%.2lf", latitude); + else if (latitude >= 10.0F) + lat = CStringUtils::string_format("00%.2lf", latitude); + else + lat = CStringUtils::string_format("000%.2lf", latitude); + + std::string lon; + if (longitude >= 10000.0F) + lon = CStringUtils::string_format("%.2lf", longitude); + else if (longitude >= 1000.0F) + lon = CStringUtils::string_format("0%.2lf", longitude); + else if (longitude >= 100.0F) + lon = CStringUtils::string_format("00%.2lf", longitude); + else if (longitude >= 10.0F) + lon = CStringUtils::string_format("000%.2lf", longitude); + else + lon = CStringUtils::string_format("0000%.2lf", longitude); + + // Convert commas to periods in the latitude and longitude + 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(), + 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()); + + + frames.push_back(output); + + 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(), + 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); + } + + setTimeout(20U * 60);//20 minutes, plenty enough for fixed + + return true; +} \ No newline at end of file diff --git a/APRSFixedIdFrameProvider.h b/APRSFixedIdFrameProvider.h new file mode 100644 index 0000000..d3bbc3d --- /dev/null +++ b/APRSFixedIdFrameProvider.h @@ -0,0 +1,30 @@ +/* + * 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 "APRSIdFrameProvider.h" + +class CAPRSFixedIdFrameProvider : public CAPRSIdFrameProvider +{ +public: + CAPRSFixedIdFrameProvider(); + +protected: + virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames); +}; diff --git a/APRSGPSDIdFrameProvider.cpp b/APRSGPSDIdFrameProvider.cpp new file mode 100644 index 0000000..6410088 --- /dev/null +++ b/APRSGPSDIdFrameProvider.cpp @@ -0,0 +1,225 @@ +/* + * 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 + +#include "APRSGPSDIdFrameProvider.h" +#include "StringUtils.h" +#include "Log.h" + +CAPRSGPSDIdFrameProvider::CAPRSGPSDIdFrameProvider(std::string address, std::string port) : +CAPRSIdFrameProvider(20U), +m_gpsdAddress(address), +m_gpsdPort(port), +m_gpsdData(), +m_hasConnection(false) +{ + +} + +void CAPRSGPSDIdFrameProvider::start() +{ + int ret = ::gps_open(m_gpsdAddress.c_str(), m_gpsdPort.c_str(), &m_gpsdData); + if (ret != 0) { + CLog::logError("Error when opening access to gpsd - %d - %s", errno, ::gps_errstr(errno)); + m_hasConnection = false; + } + else { + ::gps_stream(&m_gpsdData, WATCH_ENABLE | WATCH_JSON, NULL); + CLog::logError("Connected to GPSD"); + m_hasConnection = true; + } +} + +void CAPRSGPSDIdFrameProvider::close() +{ + if(m_hasConnection) { + ::gps_stream(&m_gpsdData, WATCH_DISABLE, NULL); + ::gps_close(&m_gpsdData); + } +} + +bool CAPRSGPSDIdFrameProvider::buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * entry, std::vector& frames) +{ + if (!::gps_waiting(&m_gpsdData, 0)) + return false; + +#if GPSD_API_MAJOR_VERSION >= 7 + char message[1024]; + if (::gps_read(&m_gpsdData, message, 1024) <= 0) + return false; +#else + if (::gps_read(&m_gpsdData) <= 0) + return false; +#endif + +#if GPSD_API_MAJOR_VERSION >= 10 + if(m_gpsdData.fix.status == STATUS_NO_FIX) + return false; +#else + if (m_gpsdData.status != STATUS_FIX) + return false; +#endif + + bool latlonSet = (m_gpsdData.set & LATLON_SET) == LATLON_SET; + bool altitudeSet = (m_gpsdData.set & ALTITUDE_SET) == ALTITUDE_SET; + bool velocitySet = (m_gpsdData.set & SPEED_SET) == SPEED_SET; + bool bearingSet = (m_gpsdData.set & TRACK_SET) == TRACK_SET; + + if (!latlonSet) + return false; + + float rawLatitude = float(m_gpsdData.fix.latitude); + float rawLongitude = float(m_gpsdData.fix.longitude); +#if GPSD_API_MAJOR_VERSION >= 9 + float rawAltitude = float(m_gpsdData.fix.altMSL); +#else + float rawAltitude = float(m_gpsdData.fix.altitude); +#endif + float rawVelocity = float(m_gpsdData.fix.speed); + float rawBearing = float(m_gpsdData.fix.track); + + time_t now; + ::time(&now); + struct tm* tm = ::gmtime(&now); + if (entry == NULL) + return false; + + std::string desc; + if (entry->getBand().length() > 1U) { + if (entry->getFrequency() != 0.0) + desc = CStringUtils::string_format("Data %.5lfMHz", entry->getFrequency()); + else + desc = "Data"; + } else { + if (entry->getFrequency() != 0.0) + desc = CStringUtils::string_format("Voice %.5lfMHz %c%.4lfMHz", + entry->getFrequency(), + entry->getOffset() < 0.0 ? '-' : '+', + ::fabs(entry->getOffset())); + else + desc = "Voice"; + } + + std::string band; + if (entry->getFrequency() >= 1200.0) + band = "1.2"; + else if (entry->getFrequency() >= 420.0) + band = "440"; + else if (entry->getFrequency() >= 144.0) + band = "2m"; + else if (entry->getFrequency() >= 50.0) + band = "6m"; + else if (entry->getFrequency() >= 28.0) + band = "10m"; + + double tempLat = ::fabs(rawLatitude); + double tempLong = ::fabs(rawLongitude); + + double latitude = ::floor(tempLat); + double longitude = ::floor(tempLong); + + latitude = (tempLat - latitude) * 60.0 + latitude * 100.0; + longitude = (tempLong - longitude) * 60.0 + longitude * 100.0; + + std::string lat; + if (latitude >= 1000.0F) + lat = CStringUtils::string_format("%.2lf", latitude); + else if (latitude >= 100.0F) + lat = CStringUtils::string_format("0%.2lf", latitude); + else if (latitude >= 10.0F) + lat = CStringUtils::string_format("00%.2lf", latitude); + else + lat = CStringUtils::string_format("000%.2lf", latitude); + + std::string lon; + if (longitude >= 10000.0F) + lon = CStringUtils::string_format("%.2lf", longitude); + else if (longitude >= 1000.0F) + lon = CStringUtils::string_format("0%.2lf", longitude); + else if (longitude >= 100.0F) + lon = CStringUtils::string_format("00%.2lf", longitude); + else if (longitude >= 10.0F) + lon = CStringUtils::string_format("000%.2lf", longitude); + else + lon = CStringUtils::string_format("0000%.2lf", longitude); + + // Convert commas to periods in the latitude and longitude + 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(), + 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); + + std::string output3; + output3 = CStringUtils::string_format("RNG%04.0lf %s %s\r\n", entry->getRange() * 0.6214, band.c_str(), desc.c_str()); + + char ascii[300U]; + ::memset(ascii, 0x00, 300U); + unsigned int n = 0U; + for (unsigned int i = 0U; i < output1.length(); i++, n++) + ascii[n] = output1[i]; + for (unsigned int i = 0U; i < output2.length(); i++, n++) + ascii[n] = output2[i]; + for (unsigned int i = 0U; i < output3.length(); i++, n++) + ascii[n] = output3[i]; + + CLog::logDebug("APRS ==> %s%s%s", output1.c_str(), output2.c_str(), output3.c_str()); + + frames.push_back(output1.append(output2).append(output3)); + + 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(), + 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(), + lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N', + lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E'); + + ::memset(ascii, 0x00, 300U); + unsigned int n = 0U; + for (unsigned int i = 0U; i < output1.length(); i++, n++) + ascii[n] = output1[i]; + for (unsigned int i = 0U; i < output2.length(); i++, n++) + ascii[n] = output2[i]; + for (unsigned int i = 0U; i < output3.length(); i++, n++) + ascii[n] = output3[i]; + + CLog::logDebug("APRS ==> %s%s%s", output1.c_str(), output2.c_str(), output3.c_str()); + + frames.push_back(output1.append(output2).append(output3)); + } + + setTimeout(60U * 5U);//5 Minutes is plenty enough we aint an APRS tracker ! + + return true; +} \ No newline at end of file diff --git a/APRSGPSDIdFrameProvider.h b/APRSGPSDIdFrameProvider.h new file mode 100644 index 0000000..77ca4b9 --- /dev/null +++ b/APRSGPSDIdFrameProvider.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 "Defs.h" + +#ifdef USE_GPSD +#include +#include + +#include "APRSIdFrameProvider.h" + +class CAPRSGPSDIdFrameProvider : public CAPRSIdFrameProvider +{ +public: + CAPRSGPSDIdFrameProvider(std::string address, std::string port); + + virtual void start(); + virtual void close(); + +protected: + virtual bool buildAPRSFramesInt(const std::string& gateway, const CAPRSEntry * aprsEntry, std::vector& frames); + +private: + std::string m_gpsdAddress; + std::string m_gpsdPort; + struct gps_data_t m_gpsdData; + bool m_hasConnection; +}; +#endif diff --git a/APRSIdFrameProvider.cpp b/APRSIdFrameProvider.cpp new file mode 100644 index 0000000..503720a --- /dev/null +++ b/APRSIdFrameProvider.cpp @@ -0,0 +1,50 @@ +/* + * 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 "APRSIdFrameProvider.h" + +CAPRSIdFrameProvider::CAPRSIdFrameProvider(unsigned int timeout) : +m_timer(1000U) +{ + m_timer.start(timeout); +} + +CAPRSIdFrameProvider::~CAPRSIdFrameProvider() +{ + +} + +bool CAPRSIdFrameProvider::buildAPRSFrames(const std::string& gateway, const CAPRSEntry * entry, std::vector & frames) +{ + assert(entry != nullptr); + + return buildAPRSFramesInt(gateway, entry, frames); +} + +bool CAPRSIdFrameProvider::wantsToSend() +{ + if(m_timer.hasExpired()) + { + m_timer.start(); + return true; + } + + return false; +} diff --git a/APRSIdFrameProvider.h b/APRSIdFrameProvider.h new file mode 100644 index 0000000..f83deb7 --- /dev/null +++ b/APRSIdFrameProvider.h @@ -0,0 +1,48 @@ +/* + * 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 "Timer.h" +#include "APRSEntry.h" + +class CAPRSIdFrameProvider +{ +public: + CAPRSIdFrameProvider(unsigned int timeOut); + virtual ~CAPRSIdFrameProvider(); + + 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; + + void setTimeout(unsigned int timeout) + { + m_timer.start(timeout); + } + +private: + CTimer m_timer; +}; \ No newline at end of file diff --git a/APRSWriter.cpp b/APRSWriter.cpp index aebbb4a..0e3ee0c 100644 --- a/APRSWriter.cpp +++ b/APRSWriter.cpp @@ -31,17 +31,10 @@ 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_idTimer(1000U), m_gateway(), m_address(), m_port(0U), m_array() -#ifdef USE_GPSD -, m_gpsdEnabled(false), -m_gpsdAddress(), -m_gpsdPort(), -m_gpsdData() -#endif { assert(!hostname.empty()); assert(port > 0U); @@ -73,45 +66,8 @@ void CAPRSWriter::setPortFixed(const std::string& callsign, const std::string& b m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, latitude, longitude, agl); } -#ifdef USE_GPSD -void CAPRSWriter::setPortGPSD(const std::string& callsign, const std::string& band, double frequency, double offset, double range, const std::string& address, const std::string& port) -{ - assert(!address.empty()); - assert(!port.empty()); - - std::string temp = callsign; - temp.resize(LONG_CALLSIGN_LENGTH - 1U, ' '); - temp.append(band); - - m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, 0.0, 0.0, 0.0); - - m_gpsdEnabled = true; - m_gpsdAddress = address; - m_gpsdPort = port; -} -#endif - bool CAPRSWriter::open() { - m_idTimer.setTimeout(20U * 60U); -#ifdef USE_GPSD - if (m_gpsdEnabled) { - int ret = ::gps_open(m_gpsdAddress.c_str(), m_gpsdPort.c_str(), &m_gpsdData); - if (ret != 0) { - CLog::logError("Error when opening access to gpsd - %d - %s", errno, ::gps_errstr(errno)); - return false; - } - - ::gps_stream(&m_gpsdData, WATCH_ENABLE | WATCH_JSON, NULL); - - CLog::logError("Connected to GPSD"); - - m_idTimer.setTimeout(60U); - } -#endif - - m_idTimer.start(); - return m_thread->start(); } @@ -203,26 +159,22 @@ void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) void CAPRSWriter::clock(unsigned int ms) { - m_idTimer.clock(ms); - m_thread->clock(ms); -#ifdef USE_GPSD - if (m_gpsdEnabled) { - if (m_idTimer.hasExpired()) { - sendIdFramesMobile(); - m_idTimer.start(); - } - - } else { -#endif - if (m_idTimer.hasExpired()) { - sendIdFramesFixed(); - m_idTimer.start(); + if(m_idFrameProvider != nullptr) { + m_idFrameProvider->clock(ms); + + if(m_idFrameProvider->wantsToSend() && 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()); + } + } + } } -#ifdef USE_GPSD } -#endif for (auto it = m_array.begin(); it != m_array.end(); ++it) { if(it->second != NULL) @@ -237,301 +189,11 @@ bool CAPRSWriter::isConnected() const void CAPRSWriter::close() { -#ifdef USE_GPSD - if (m_gpsdEnabled) { - ::gps_stream(&m_gpsdData, WATCH_DISABLE, NULL); - ::gps_close(&m_gpsdData); + if(m_idFrameProvider != nullptr) { + m_idFrameProvider->close(); + delete m_idFrameProvider; + m_idFrameProvider = nullptr; } -#endif m_thread->stop(); } - -void CAPRSWriter::sendIdFramesFixed() -{ - if (!m_thread->isConnected()) - return; - - time_t now; - ::time(&now); - struct tm* tm = ::gmtime(&now); - - for (auto it = m_array.begin(); it != m_array.end(); ++it) { - CAPRSEntry* entry = it->second; - if (entry == NULL) - continue; - - // Default values aren't passed on - if (entry->getLatitude() == 0.0 && entry->getLongitude() == 0.0) - continue; - - std::string desc; - if (entry->getBand().length() > 1U) { - if (entry->getFrequency() != 0.0) - desc = CStringUtils::string_format("Data %.5lfMHz", entry->getFrequency()); - else - desc = "Data"; - } else { - if (entry->getFrequency() != 0.0) - desc = CStringUtils::string_format("Voice %.5lfMHz %c%.4lfMHz", - entry->getFrequency(), - entry->getOffset() < 0.0 ? '-' : '+', - ::fabs(entry->getOffset())); - else - desc = "Voice"; - } - - std::string band; - if (entry->getFrequency() >= 1200.0) - band = "1.2"; - else if (entry->getFrequency() >= 420.0) - band = "440"; - else if (entry->getFrequency() >= 144.0) - band = "2m"; - else if (entry->getFrequency() >= 50.0) - band = "6m"; - else if (entry->getFrequency() >= 28.0) - band = "10m"; - - double tempLat = ::fabs(entry->getLatitude()); - double tempLong = ::fabs(entry->getLongitude()); - - double latitude = ::floor(tempLat); - double longitude = ::floor(tempLong); - - latitude = (tempLat - latitude) * 60.0 + latitude * 100.0; - longitude = (tempLong - longitude) * 60.0 + longitude * 100.0; - - std::string lat; - if (latitude >= 1000.0F) - lat = CStringUtils::string_format("%.2lf", latitude); - else if (latitude >= 100.0F) - lat = CStringUtils::string_format("0%.2lf", latitude); - else if (latitude >= 10.0F) - lat = CStringUtils::string_format("00%.2lf", latitude); - else - lat = CStringUtils::string_format("000%.2lf", latitude); - - std::string lon; - if (longitude >= 10000.0F) - lon = CStringUtils::string_format("%.2lf", longitude); - else if (longitude >= 1000.0F) - lon = CStringUtils::string_format("0%.2lf", longitude); - else if (longitude >= 100.0F) - lon = CStringUtils::string_format("00%.2lf", longitude); - else if (longitude >= 10.0F) - lon = CStringUtils::string_format("000%.2lf", longitude); - else - lon = CStringUtils::string_format("0000%.2lf", longitude); - - // Convert commas to periods in the latitude and longitude - boost::replace_all(lat, ",", "."); - boost::replace_all(lon, ",", "."); - - std::string output; - 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", - m_gateway.c_str(), m_gateway.c_str(), 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()); - - char ascii[300U]; - ::memset(ascii, 0x00, 300U); - for (unsigned int i = 0U; i < output.length(); i++) - ascii[i] = output[i]; - - m_thread->write(ascii); - - 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", - entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), - 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()); - - ::memset(ascii, 0x00, 300U); - for (unsigned int i = 0U; i < output.length(); i++) - ascii[i] = output[i]; - - m_thread->write(ascii); - } - } -} - -#ifdef USE_GPSD -void CAPRSWriter::sendIdFramesMobile() -{ - if (!m_thread->isConnected()) - return; - - if (!m_gpsdEnabled) - return; - - if (!::gps_waiting(&m_gpsdData, 0)) - return; - -#if GPSD_API_MAJOR_VERSION >= 7 - if (::gps_read(&m_gpsdData, NULL, 0) <= 0) - return; -#else - if (::gps_read(&m_gpsdData) <= 0) - return; -#endif - -#if GPSD_API_MAJOR_VERSION >= 11 // F4FXL 2021-12-31 not sure if 11 is porper version, best guess as i could not find any change log - if(m_gpsdData.fix.mode < MODE_3D) - return; -#else - if (m_gpsdData.status != STATUS_FIX) - return; -#endif - - bool latlonSet = (m_gpsdData.set & LATLON_SET) == LATLON_SET; - bool altitudeSet = (m_gpsdData.set & ALTITUDE_SET) == ALTITUDE_SET; - bool velocitySet = (m_gpsdData.set & SPEED_SET) == SPEED_SET; - bool bearingSet = (m_gpsdData.set & TRACK_SET) == TRACK_SET; - - if (!latlonSet) - return; - - float rawLatitude = float(m_gpsdData.fix.latitude); - float rawLongitude = float(m_gpsdData.fix.longitude); -#if GPSD_API_MAJOR_VERSION >= 9 - float rawAltitude = float(m_gpsdData.fix.altMSL); -#else - float rawAltitude = float(m_gpsdData.fix.altitude); -#endif - float rawVelocity = float(m_gpsdData.fix.speed); - float rawBearing = float(m_gpsdData.fix.track); - - time_t now; - ::time(&now); - struct tm* tm = ::gmtime(&now); - - for (auto it = m_array.begin(); it != m_array.end(); ++it) { - CAPRSEntry* entry = it->second; - if (entry == NULL) - continue; - - std::string desc; - if (entry->getBand().length() > 1U) { - if (entry->getFrequency() != 0.0) - desc = CStringUtils::string_format("Data %.5lfMHz", entry->getFrequency()); - else - desc = "Data"; - } else { - if (entry->getFrequency() != 0.0) - desc = CStringUtils::string_format("Voice %.5lfMHz %c%.4lfMHz", - entry->getFrequency(), - entry->getOffset() < 0.0 ? '-' : '+', - ::fabs(entry->getOffset())); - else - desc = "Voice"; - } - - std::string band; - if (entry->getFrequency() >= 1200.0) - band = "1.2"; - else if (entry->getFrequency() >= 420.0) - band = "440"; - else if (entry->getFrequency() >= 144.0) - band = "2m"; - else if (entry->getFrequency() >= 50.0) - band = "6m"; - else if (entry->getFrequency() >= 28.0) - band = "10m"; - - double tempLat = ::fabs(rawLatitude); - double tempLong = ::fabs(rawLongitude); - - double latitude = ::floor(tempLat); - double longitude = ::floor(tempLong); - - latitude = (tempLat - latitude) * 60.0 + latitude * 100.0; - longitude = (tempLong - longitude) * 60.0 + longitude * 100.0; - - std::string lat; - if (latitude >= 1000.0F) - lat = CStringUtils::string_format("%.2lf", latitude); - else if (latitude >= 100.0F) - lat = CStringUtils::string_format("0%.2lf", latitude); - else if (latitude >= 10.0F) - lat = CStringUtils::string_format("00%.2lf", latitude); - else - lat = CStringUtils::string_format("000%.2lf", latitude); - - std::string lon; - if (longitude >= 10000.0F) - lon = CStringUtils::string_format("%.2lf", longitude); - else if (longitude >= 1000.0F) - lon = CStringUtils::string_format("0%.2lf", longitude); - else if (longitude >= 100.0F) - lon = CStringUtils::string_format("00%.2lf", longitude); - else if (longitude >= 10.0F) - lon = CStringUtils::string_format("000%.2lf", longitude); - else - lon = CStringUtils::string_format("0000%.2lf", longitude); - - // Convert commas to periods in the latitude and longitude - boost::replace_all(lat, ",", "."); - boost::replace_all(lon, ",", "."); - - std::string output1; - output1 = CStringUtils::string_format("%s-S>APD5T1,TCPIP*,qAC,%s-GS:;%-7s%-2s*%02d%02d%02dz%s%cD%s%ca/A=%06.0lf", - m_gateway.c_str(), m_gateway.c_str(), 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); - - std::string output3; - output3 = CStringUtils::string_format("RNG%04.0lf %s %s\r\n", entry->getRange() * 0.6214, band.c_str(), desc.c_str()); - - char ascii[300U]; - ::memset(ascii, 0x00, 300U); - unsigned int n = 0U; - for (unsigned int i = 0U; i < output1.length(); i++, n++) - ascii[n] = output1[i]; - for (unsigned int i = 0U; i < output2.length(); i++, n++) - ascii[n] = output2[i]; - for (unsigned int i = 0U; i < output3.length(); i++, n++) - ascii[n] = output3[i]; - - CLog::logDebug("APRS ==> %s%s%s", output1.c_str(), output2.c_str(), output3.c_str()); - - m_thread->write(ascii); - - 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(), - 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(), - lat.c_str(), (rawLatitude < 0.0) ? 'S' : 'N', - lon.c_str(), (rawLongitude < 0.0) ? 'W' : 'E'); - - ::memset(ascii, 0x00, 300U); - unsigned int n = 0U; - for (unsigned int i = 0U; i < output1.length(); i++, n++) - ascii[n] = output1[i]; - for (unsigned int i = 0U; i < output2.length(); i++, n++) - ascii[n] = output2[i]; - for (unsigned int i = 0U; i < output3.length(); i++, n++) - ascii[n] = output3[i]; - - CLog::logDebug("APRS ==> %s%s%s", output1.c_str(), output2.c_str(), output3.c_str()); - - m_thread->write(ascii); - } - } -} -#endif diff --git a/APRSWriter.h b/APRSWriter.h index 29a8428..ab28506 100644 --- a/APRSWriter.h +++ b/APRSWriter.h @@ -20,11 +20,11 @@ #ifndef APRSWriter_H #define APRSWriter_H +#include "Defs.h" + #include #include -#ifdef USE_GPSD -#include -#endif + #include "APRSEntry.h" #include "APRSWriterThread.h" @@ -34,7 +34,7 @@ #include "HeaderData.h" #include "AMBEData.h" #include "Timer.h" -#include "Defs.h" +#include "APRSIdFrameProvider.h" class CAPRSWriter { public: @@ -43,10 +43,10 @@ public: bool open(); + void setIdFrameProvider(CAPRSIdFrameProvider * idFrameProvider) { m_idFrameProvider = idFrameProvider; } + void setPortFixed(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl); -#ifdef USE_GPSD - void setPortGPSD(const std::string& callsign, const std::string& band, double frequency, double offset, double range, const std::string& address, const std::string& port); -#endif + void writeHeader(const std::string& callsign, const CHeaderData& header); void writeData(const std::string& callsign, const CAMBEData& data); @@ -59,21 +59,12 @@ public: private: CAPRSWriterThread* m_thread; - CTimer m_idTimer; std::string m_gateway; in_addr m_address; unsigned int m_port; std::unordered_map m_array; + CAPRSIdFrameProvider * m_idFrameProvider; -#ifdef USE_GPSD - bool m_gpsdEnabled; - std::string m_gpsdAddress; - std::string m_gpsdPort; - struct gps_data_t m_gpsdData; -#endif - - bool pollGPS(); - void sendIdFramesFixed(); void sendIdFramesMobile(); }; diff --git a/DStarGatewayApp.cpp b/DStarGatewayApp.cpp index 2668861..7fa3f18 100644 --- a/DStarGatewayApp.cpp +++ b/DStarGatewayApp.cpp @@ -38,6 +38,8 @@ #include "Log.h" #include "LogFileTarget.h" #include "LogConsoleTarget.h" +#include "APRSGPSDIdFrameProvider.h" +#include "APRSFixedIdFrameProvider.h" int main(int argc, char *argv[]) { @@ -119,19 +121,34 @@ bool CDStarGatewayApp::createThread() m_thread->setLanguage(gatewayConfig.language); m_thread->setLocation(gatewayConfig.latitude, gatewayConfig.longitude); +#ifdef USE_GPSD + // Setup GPSD + TGPSD gpsdConfig; + config.getGPSD(gpsdConfig); +#endif + // Setup APRS TAPRS aprsConfig; config.getAPRS(aprsConfig); CAPRSWriter * aprsWriter = NULL; if(aprsConfig.enabled && !aprsConfig.password.empty()) { aprsWriter = new CAPRSWriter(aprsConfig.hostname, aprsConfig.port, gatewayConfig.callsign, aprsConfig.password, gatewayConfig.address); - } - + if(aprsWriter->open()) { #ifdef USE_GPSD - // Setup GPSD - TGPSD gpsdConfig; - config.getGPSD(gpsdConfig); + CAPRSIdFrameProvider * idFrameProvider = aprsConfig.m_positionSource == POSSRC_GPSD ? (CAPRSIdFrameProvider *)new CAPRSGPSDIdFrameProvider(gpsdConfig.m_address, gpsdConfig.m_port) + : new CAPRSFixedIdFrameProvider(); +#else + CAPRSIdFrameProvider * idFrameProvider = new CAPRSFixedIdFrameProvider(); #endif + idFrameProvider->start(); + aprsWriter->setIdFrameProvider(idFrameProvider); + m_thread->setAPRSWriter(aprsWriter); + } + else { + delete aprsWriter; + aprsWriter = NULL; + } + } // Setup the repeaters if(config.getRepeaterCount() == 0U) { @@ -164,34 +181,14 @@ bool CDStarGatewayApp::createThread() rptrConfig.band1, rptrConfig.band2, rptrConfig.band3); -#ifdef USE_GPSD - if(aprsWriter != NULL) { - if(aprsConfig.m_positionSource == POSSRC_GPSD) - aprsWriter->setPortGPSD(rptrConfig.callsign, rptrConfig.band, rptrConfig.frequency, rptrConfig.offset, rptrConfig.range, gpsdConfig.m_address, gpsdConfig.m_port); - else - aprsWriter->setPortFixed(rptrConfig.callsign, rptrConfig.band, rptrConfig.frequency, rptrConfig.offset, rptrConfig.range, rptrConfig.latitude, rptrConfig.longitude, rptrConfig.agl); - } -#else aprsWriter->setPortFixed(rptrConfig.callsign, rptrConfig.band, rptrConfig.frequency, rptrConfig.offset, rptrConfig.range, rptrConfig.latitude, rptrConfig.longitude, rptrConfig.agl); -#endif if(!ddEnabled) ddEnabled = rptrConfig.band.length() > 1U; } m_thread->setDDModeEnabled(ddEnabled); CLog::logInfo("DD Mode enabled: %d", int(ddEnabled)); - // open APRS after setting the repeaters because of GPSD - if(aprsWriter != nullptr) { - if(aprsWriter->open()) { - m_thread->setAPRSWriter(aprsWriter); - } - else { - delete aprsWriter; - aprsWriter = NULL; - } - } - // Setup ircddb std::vector clients; for(unsigned int i=0; i < config.getIrcDDBCount(); i++) {