diff --git a/APRSEntry.cpp b/APRSEntry.cpp new file mode 100644 index 0000000..a9fbdcf --- /dev/null +++ b/APRSEntry.cpp @@ -0,0 +1,119 @@ +/* + * 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 "APRSEntry.h" + +CAPRSEntry::CAPRSEntry(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl) : +m_callsign(callsign), +m_band(band), +m_frequency(frequency), +m_offset(offset), +m_range(range), +m_latitude(latitude), +m_longitude(longitude), +m_agl(agl), +m_timer(1000U, 10U), +m_first(true), +m_collector(NULL) +{ + boost::trim(m_callsign); + + m_collector = new CAPRSCollector; +} + +CAPRSEntry::~CAPRSEntry() +{ + delete m_collector; +} + +std::string CAPRSEntry::getCallsign() const +{ + return m_callsign; +} + +std::string CAPRSEntry::getBand() const +{ + return m_band; +} + +double CAPRSEntry::getFrequency() const +{ + return m_frequency; +} + +double CAPRSEntry::getOffset() const +{ + return m_offset; +} + +double CAPRSEntry::getRange() const +{ + return m_range; +} + +double CAPRSEntry::getLatitude() const +{ + return m_latitude; +} + +double CAPRSEntry::getLongitude() const +{ + return m_longitude; +} + +double CAPRSEntry::getAGL() const +{ + return m_agl; +} + +CAPRSCollector* CAPRSEntry::getCollector() const +{ + return m_collector; +} + +void CAPRSEntry::reset() +{ + m_first = true; + m_timer.stop(); + m_collector->reset(); +} + +void CAPRSEntry::clock(unsigned int ms) +{ + m_timer.clock(ms); +} + +bool CAPRSEntry::isOK() +{ + if (m_first) { + m_first = false; + m_timer.start(); + return true; + } + + if (m_timer.hasExpired()) { + m_timer.start(); + return true; + } else { + m_timer.start(); + return false; + } +} \ No newline at end of file diff --git a/APRSEntry.h b/APRSEntry.h new file mode 100644 index 0000000..3ad5484 --- /dev/null +++ b/APRSEntry.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include "APRSCollector.h" +#include "Timer.h" + +class CAPRSEntry { +public: + CAPRSEntry(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl); + ~CAPRSEntry(); + + std::string getCallsign() const; + std::string getBand() const; + double getFrequency() const; + double getOffset() const; + double getRange() const; + double getLatitude() const; + double getLongitude() const; + double getAGL() const; + CAPRSCollector* getCollector() const; + + // Transmission timer + void reset(); + void clock(unsigned int ms); + bool isOK(); + +private: + std::string m_callsign; + std::string m_band; + double m_frequency; + double m_offset; + double m_range; + double m_latitude; + double m_longitude; + double m_agl; + CTimer m_timer; + bool m_first; + CAPRSCollector* m_collector; +}; 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..3a02916 --- /dev/null +++ b/APRSGPSDIdFrameProvider.cpp @@ -0,0 +1,212 @@ +/* + * 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. + */ + +#ifdef USE_DGPS +#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(!m_hasConnection) { + this->start(); + } + + if(!m_hasConnection || !::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()); + + 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'); + + 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; +} +#endif \ 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 63df80d..12d0272 100644 --- a/APRSWriter.cpp +++ b/APRSWriter.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "StringUtils.h" #include "Log.h" @@ -28,110 +29,11 @@ #include "Defs.h" #include "Log.h" -CAPRSEntry::CAPRSEntry(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl) : -m_callsign(callsign), -m_band(band), -m_frequency(frequency), -m_offset(offset), -m_range(range), -m_latitude(latitude), -m_longitude(longitude), -m_agl(agl), -m_timer(1000U, 10U), -m_first(true), -m_collector(NULL) -{ - boost::trim(m_callsign); - - m_collector = new CAPRSCollector; -} - -CAPRSEntry::~CAPRSEntry() -{ - delete m_collector; -} - -std::string CAPRSEntry::getCallsign() const -{ - return m_callsign; -} - -std::string CAPRSEntry::getBand() const -{ - return m_band; -} - -double CAPRSEntry::getFrequency() const -{ - return m_frequency; -} - -double CAPRSEntry::getOffset() const -{ - return m_offset; -} - -double CAPRSEntry::getRange() const -{ - return m_range; -} - -double CAPRSEntry::getLatitude() const -{ - return m_latitude; -} - -double CAPRSEntry::getLongitude() const -{ - return m_longitude; -} - -double CAPRSEntry::getAGL() const -{ - return m_agl; -} - -CAPRSCollector* CAPRSEntry::getCollector() const -{ - return m_collector; -} - -void CAPRSEntry::reset() -{ - m_first = true; - m_timer.stop(); - m_collector->reset(); -} - -void CAPRSEntry::clock(unsigned int ms) -{ - m_timer.clock(ms); -} - -bool CAPRSEntry::isOK() -{ - if (m_first) { - m_first = false; - m_timer.start(); - return true; - } - - if (m_timer.hasExpired()) { - m_timer.start(); - return true; - } else { - m_timer.start(); - return false; - } -} - 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_socket(NULL), m_array() { assert(!hostname.empty()); @@ -155,7 +57,7 @@ CAPRSWriter::~CAPRSWriter() m_array.clear(); } -void CAPRSWriter::setPortFixed(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl) +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, ' '); @@ -164,40 +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); } -void CAPRSWriter::setPortMobile(const std::string& callsign, const std::string& band, double frequency, double offset, double range, const std::string& address, unsigned int port) -{ - std::string temp = callsign; - temp.resize(LONG_CALLSIGN_LENGTH - 1U, ' '); - temp += band; - - m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, 0.0, 0.0, 0.0); - - if (m_socket == NULL) { - m_address = CUDPReaderWriter::lookup(address); - m_port = port; - - m_socket = new CUDPReaderWriter(); - } -} - bool CAPRSWriter::open() { - if (m_socket != NULL) { - bool ret = m_socket->open(); - if (!ret) { - delete m_socket; - m_socket = NULL; - return false; - } - - // Poll the GPS every minute - m_idTimer.setTimeout(60U); - } else { - m_idTimer.setTimeout(20U * 60U); - } - - m_idTimer.start(); - return m_thread->start(); } @@ -289,21 +159,20 @@ void CAPRSWriter::writeData(const std::string& callsign, const CAMBEData& data) void CAPRSWriter::clock(unsigned int ms) { - m_idTimer.clock(ms); - m_thread->clock(ms); - if (m_socket != NULL) { - if (m_idTimer.hasExpired()) { - pollGPS(); - m_idTimer.start(); - } - - sendIdFramesMobile(); - } else { - 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()); + } + } + } } } @@ -320,283 +189,11 @@ bool CAPRSWriter::isConnected() const void CAPRSWriter::close() { - if (m_socket != NULL) { - m_socket->close(); - delete m_socket; + if(m_idFrameProvider != nullptr) { + m_idFrameProvider->close(); + delete m_idFrameProvider; + m_idFrameProvider = nullptr; } m_thread->stop(); } - -bool CAPRSWriter::pollGPS() -{ - assert(m_socket != NULL); - - return m_socket->write((unsigned char*)"ircDDBGateway", 13U, m_address, m_port); -} - -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); - } - } -} - -void CAPRSWriter::sendIdFramesMobile() -{ - // Grab GPS data if it's available - unsigned char buffer[200U]; - in_addr address; - unsigned int port; - int ret = m_socket->read(buffer, 200U, address, port); - if (ret <= 0) - return; - - if (!m_thread->isConnected()) - return; - - buffer[ret] = '\0'; - - // Parse the GPS data - char* pLatitude = ::strtok((char*)buffer, ",\n"); // Latitude - char* pLongitude = ::strtok(NULL, ",\n"); // Longitude - char* pAltitude = ::strtok(NULL, ",\n"); // Altitude (m) - char* pVelocity = ::strtok(NULL, ",\n"); // Velocity (kms/h) - char* pBearing = ::strtok(NULL, "\n"); // Bearing - - if (pLatitude == NULL || pLongitude == NULL || pAltitude == NULL) - return; - - double rawLatitude = ::atof(pLatitude); - double rawLongitude = ::atof(pLongitude); - double rawAltitude = ::atof(pAltitude); - - 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 (pBearing != NULL && pVelocity != NULL) { - double rawBearing = ::atof(pBearing); - double rawVelocity = ::atof(pVelocity); - - output2 = CStringUtils::string_format("%03.0lf/%03.0lf", rawBearing, rawVelocity * 0.539957F); - } - - std::string output3; - output3 = CStringUtils::string_format("RNG%04.0lf %s %s", 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]; - - m_thread->write(ascii); - - if (entry->getBand().length() == 1U) { - 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); - - ::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]; - - m_thread->write(ascii); - } - } -} - diff --git a/APRSWriter.h b/APRSWriter.h index 54c672c..b49adcf 100644 --- a/APRSWriter.h +++ b/APRSWriter.h @@ -20,9 +20,13 @@ #ifndef APRSWriter_H #define APRSWriter_H +#include "Defs.h" + #include #include + +#include "APRSEntry.h" #include "APRSWriterThread.h" #include "UDPReaderWriter.h" #include "APRSCollector.h" @@ -30,41 +34,7 @@ #include "HeaderData.h" #include "AMBEData.h" #include "Timer.h" -#include "Defs.h" - -class CAPRSEntry { -public: - CAPRSEntry(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl); - ~CAPRSEntry(); - - std::string getCallsign() const; - std::string getBand() const; - double getFrequency() const; - double getOffset() const; - double getRange() const; - double getLatitude() const; - double getLongitude() const; - double getAGL() const; - CAPRSCollector* getCollector() const; - - // Transmission timer - void reset(); - void clock(unsigned int ms); - bool isOK(); - -private: - std::string m_callsign; - std::string m_band; - double m_frequency; - double m_offset; - double m_range; - double m_latitude; - double m_longitude; - double m_agl; - CTimer m_timer; - bool m_first; - CAPRSCollector* m_collector; -}; +#include "APRSIdFrameProvider.h" class CAPRSWriter { public: @@ -73,9 +43,9 @@ public: bool open(); - void setPortFixed(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl); + void setIdFrameProvider(CAPRSIdFrameProvider * idFrameProvider) { m_idFrameProvider = idFrameProvider; } - void setPortMobile(const std::string& callsign, const std::string& band, double frequency, double offset, double range, const std::string& address, unsigned int port); + void setPort(const std::string& callsign, const std::string& band, double frequency, double offset, double range, double latitude, double longitude, double agl); void writeHeader(const std::string& callsign, const CHeaderData& header); @@ -89,15 +59,12 @@ public: private: CAPRSWriterThread* m_thread; - CTimer m_idTimer; std::string m_gateway; in_addr m_address; unsigned int m_port; - CUDPReaderWriter* m_socket; std::unordered_map m_array; + CAPRSIdFrameProvider * m_idFrameProvider; - bool pollGPS(); - void sendIdFramesFixed(); void sendIdFramesMobile(); }; diff --git a/APRSWriterThread.cpp b/APRSWriterThread.cpp index 2e2c137..fe27a9b 100644 --- a/APRSWriterThread.cpp +++ b/APRSWriterThread.cpp @@ -26,6 +26,7 @@ #include "Utils.h" #include "Defs.h" #include "Log.h" +#include "Version.h" // #define DUMP_TX @@ -44,7 +45,7 @@ m_reconnectTimer(1000U), m_tries(0U), m_APRSReadCallback(NULL), m_filter(""), -m_clientName("DStarGateway") +m_clientName(FULL_PRODUCT_NAME) { assert(!callsign.empty()); assert(!password.empty()); @@ -154,7 +155,7 @@ void* CAPRSWriterThread::Entry() if (length < 0) { m_connected = false; m_socket.close(); - CLog::logInfo("Error when reading from the APRS server"); + CLog::logError("Error when reading from the APRS server"); startReconnectionTimer(); } @@ -249,7 +250,7 @@ bool CAPRSWriterThread::connect() std::stringstream connectString; connectString << "User " << m_username << "-" << m_ssid << " pass " << m_password - << " vers " << (m_clientName.length() ? m_clientName : "DStarGateway") + << " vers " << (!m_clientName.empty() ? m_clientName : FULL_PRODUCT_NAME) << filter; //CLog::logInfo("Connect String : ") + connectString); ret = m_socket.writeLine(connectString.str()); diff --git a/DStarGatewayApp.cpp b/DStarGatewayApp.cpp index 33c6239..c8fd520 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[]) { @@ -49,7 +51,7 @@ int main(int argc, char *argv[]) } if ('-' == argv[1][0]) { - printf("\nDStarGateway Version %s (GitID #%.7s) Copyright (C) %s\n", VERSION.c_str(), gitversion, VENDOR_NAME.c_str()); + printf("\n%s Copyright (C) %s\n", FULL_PRODUCT_NAME.c_str(), VENDOR_NAME.c_str()); printf("DStarGateway comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n"); return 0; @@ -91,7 +93,7 @@ void CDStarGatewayApp::run() bool CDStarGatewayApp::createThread() { - printf("\nDStarGateway Version %s (GitID #%.7s) Copyright (C) %s\n", VERSION.c_str(), gitversion, VENDOR_NAME.c_str()); + printf("\n%s Copyright (C) %s\n", FULL_PRODUCT_NAME.c_str(), VENDOR_NAME.c_str()); printf("DStarGateway comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n"); @@ -119,6 +121,12 @@ 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); @@ -126,6 +134,14 @@ bool CDStarGatewayApp::createThread() 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 + 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 { @@ -165,8 +181,9 @@ bool CDStarGatewayApp::createThread() rptrConfig.band1, rptrConfig.band2, rptrConfig.band3); - - if(aprsWriter != NULL) aprsWriter->setPortFixed(rptrConfig.callsign, rptrConfig.band, rptrConfig.frequency, rptrConfig.offset, rptrConfig.range, rptrConfig.latitude, rptrConfig.longitude, rptrConfig.agl); + + aprsWriter->setPort(rptrConfig.callsign, rptrConfig.band, rptrConfig.frequency, rptrConfig.offset, rptrConfig.range, rptrConfig.latitude, rptrConfig.longitude, rptrConfig.agl); + if(!ddEnabled) ddEnabled = rptrConfig.band.length() > 1U; } m_thread->setDDModeEnabled(ddEnabled); @@ -178,7 +195,7 @@ bool CDStarGatewayApp::createThread() TircDDB ircDDBConfig; config.getIrcDDB(i, ircDDBConfig); CLog::logInfo("ircDDB Network %d set to %s user: %s, Quadnet %d", i + 1,ircDDBConfig.hostname.c_str(), ircDDBConfig.username.c_str(), ircDDBConfig.isQuadNet); - CIRCDDB * ircDDB = new CIRCDDBClient(ircDDBConfig.hostname, 9007U, ircDDBConfig.username, ircDDBConfig.password, std::string("DStarGateway") + std::string("-") + VERSION, gatewayConfig.address, ircDDBConfig.isQuadNet); + CIRCDDB * ircDDB = new CIRCDDBClient(ircDDBConfig.hostname, 9007U, ircDDBConfig.username, ircDDBConfig.password, FULL_PRODUCT_NAME, gatewayConfig.address, ircDDBConfig.isQuadNet); clients.push_back(ircDDB); } CIRCDDBMultiClient* multiClient = new CIRCDDBMultiClient(clients); diff --git a/DStarGatewayConfig.cpp b/DStarGatewayConfig.cpp index d2ad9af..a976c1f 100644 --- a/DStarGatewayConfig.cpp +++ b/DStarGatewayConfig.cpp @@ -52,6 +52,9 @@ bool CDStarGatewayConfig::load() ret = loadDPlus(cfg) && ret; ret = loadRemote(cfg) && ret; ret = loadXLX(cfg) && ret; +#ifdef USE_GPSD + ret = loadGPSD(cfg) && ret; +#endif } if(ret) { @@ -118,6 +121,16 @@ bool CDStarGatewayConfig::loadAPRS(const CConfig & cfg) ret = cfg.getValue("aprs", "port", m_aprs.port, 1U, 65535U, 14580U) && ret; ret = cfg.getValue("aprs", "hostname", m_aprs.hostname, 0, 1024, "rotate.aprs2.net") && ret; ret = cfg.getValue("aprs", "password", m_aprs.password, 0U, 30U, "") && ret; +#ifdef USE_GPSD + std::string positionSource; + ret = cfg.getValue("aprs", "positionSource", positionSource, "fixed", {"fixed", "gpsd"}) && ret; + if(ret) { + if(positionSource == "fixed") m_aprs.m_positionSource = POSSRC_FIXED; + else if(positionSource == "gpsd") m_aprs.m_positionSource = POSSRC_GPSD; + } +#else + m_aprs.m_positionSource = POSSRC_FIXED; +#endif m_aprs.enabled = m_aprs.enabled && !m_aprs.password.empty(); @@ -319,6 +332,16 @@ bool CDStarGatewayConfig::loadGateway(const CConfig & cfg) return ret; } +#ifdef USE_GPSD +bool CDStarGatewayConfig::loadGPSD(const CConfig & cfg) +{ + bool ret = cfg.getValue("gpsd", "address", m_gpsd.m_address, 0U, 15U, "127.0.0.1"); + ret = cfg.getValue("gpsd", "port", m_gpsd.m_port, 0U, 5U, "2947") && ret; + + return ret; +} +#endif + bool CDStarGatewayConfig::open(CConfig & cfg) { try { @@ -408,3 +431,10 @@ void CDStarGatewayConfig::getXLX(TXLX & xlx) const { xlx = m_xlx; } + +#ifdef USE_GPSD +void CDStarGatewayConfig::getGPSD(TGPSD & gpsd) const +{ + gpsd = m_gpsd; +} +#endif diff --git a/DStarGatewayConfig.h b/DStarGatewayConfig.h index a406dc0..b27ca44 100644 --- a/DStarGatewayConfig.h +++ b/DStarGatewayConfig.h @@ -91,6 +91,7 @@ typedef struct { std::string hostname; unsigned int port; std::string password; + POSITION_SOURCE m_positionSource; } TAPRS; typedef struct { @@ -120,6 +121,12 @@ typedef struct { std::string password; } TRemote; +#ifdef USE_GPSD +typedef struct { + std::string m_address; + std::string m_port; +} TGPSD; +#endif class CDStarGatewayConfig { public: @@ -140,6 +147,9 @@ public: void getDCS(TDCS & dcs) const; void getRemote(TRemote & remote) const; void getXLX(TXLX & xlx) const; +#ifdef USE_GPSD + void getGPSD(TGPSD & gpsd) const; +#endif private: bool open(CConfig & cfg); @@ -154,6 +164,9 @@ private: bool loadDCS(const CConfig & cfg); bool loadRemote(const CConfig & cfg); bool loadXLX(const CConfig & cfg); +#ifdef USE_GPSD + bool loadGPSD(const CConfig & cfg); +#endif std::string m_fileName; TGateway m_gateway; @@ -165,6 +178,9 @@ private: TRemote m_remote; TXLX m_xlx; TLog m_log; +#ifdef USE_GPSD + TGPSD m_gpsd; +#endif std::vector m_repeaters; std::vector m_ircDDB; }; diff --git a/DStarGatewayThread.cpp b/DStarGatewayThread.cpp index 70fbb69..23aa7a2 100644 --- a/DStarGatewayThread.cpp +++ b/DStarGatewayThread.cpp @@ -751,7 +751,7 @@ void CDStarGatewayThread::processIrcDDB() m_natTraversal->traverseNatG2(address); #endif } else { - CLog::logInfo("USER: %s NOT FOUND", user.c_str()); + CLog::logDebug("USER: %s NOT FOUND", user.c_str()); } } break; @@ -770,7 +770,7 @@ void CDStarGatewayThread::processIrcDDB() m_natTraversal->traverseNatG2(address); #endif } else { - CLog::logInfo("REPEATER: %s NOT FOUND", repeater.c_str()); + CLog::logDebug("REPEATER: %s NOT FOUND", repeater.c_str()); } } break; @@ -790,7 +790,7 @@ void CDStarGatewayThread::processIrcDDB() m_natTraversal->traverseNatG2(address); #endif } else { - CLog::logInfo("GATEWAY: %s NOT FOUND", gateway.c_str()); + CLog::logDebug("GATEWAY: %s NOT FOUND", gateway.c_str()); } } break; diff --git a/Defs.h b/Defs.h index 273ed25..2f12568 100644 --- a/Defs.h +++ b/Defs.h @@ -137,5 +137,10 @@ enum GATEWAY_TYPE { GT_SMARTGROUP }; +enum POSITION_SOURCE { + POSSRC_FIXED, + POSSRC_GPSD +}; + const unsigned int TIME_PER_TIC_MS = 5U; diff --git a/Makefile b/Makefile index 3e1e2e4..d959810 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,13 @@ CPPFLAGS=-g -ggdb -W -Wall -std=c++17 # or, you can choose this for a much smaller executable without debugging help #CPPFLAGS=-W -Wall -std=c++17 +LDFLAGS:=-lcurl -pthread + +ifeq ($(USE_GPSD), 1) +CPPFLAGS+= -DUSE_GPSD +LDFLAGS+= -lgps +endif + SRCS = $(wildcard *.cpp) OBJS = $(SRCS:.cpp=.o) DEPS = $(SRCS:.cpp=.d) @@ -34,16 +41,20 @@ DEPS = $(SRCS:.cpp=.d) all: dstargateway dstargateway : GitVersion.h $(OBJS) - g++ $(CPPFLAGS) -o dstargateway $(OBJS) -lcurl -pthread + g++ $(CPPFLAGS) -o dstargateway $(OBJS) $(LDFLAGS) %.o : %.cpp g++ $(CPPFLAGS) -MMD -MD -c $< -o $@ GitVersion.h : FORCE ifneq ("$(wildcard .git/index)","") - @echo "#pragma once\nconst char *gitversion = \"$(shell git rev-parse HEAD)\";" > /tmp/$@ + @echo "#pragma once" > /tmp/$@ + @echo "#include " >> /tmp/$@ + @echo "const std::string gitversion(\"$(shell git rev-parse --short HEAD)\");" >> /tmp/$@ else - @echo "#pragma once\nconst char *gitversion = \"0000000000000000000000000000000000000000\";" > /tmp/$@ + @echo "#pragma once" > /tmp/$@ + @echo "#include " >> /tmp/$@ + @echo "const std::string gitversion(\"0000000\");" >> /tmp/$@ endif @cmp -s /tmp/$@ $@; \ RETVAL=$$?; \ diff --git a/README.md b/README.md index c8e35b5..4081e26 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,11 @@ - [2.3. Features](#23-features) - [3. Building and installing](#3-building-and-installing) - [3.1. Initial setup](#31-initial-setup) - - [3.2. Get latest stable version](#32-get-latest-stable-version) + - [3.2. Get latest stable version (recommended)](#32-get-latest-stable-version-recommended) - [3.3. Get latest development version version](#33-get-latest-development-version-version) - [3.4. Prerequisites and dependencies](#34-prerequisites-and-dependencies) - [3.5. Building](#35-building) + - [Build With GPSD Support](#build-with-gpsd-support) - [3.6. Installing](#36-installing) - [3.7. Configuring](#37-configuring) - [4. Contributing](#4-contributing) @@ -41,8 +42,7 @@ Features that where left out : - CCS: is still being used? I always considered this as trojan horse to push some DMR Agenda into DStar an more or les a burdain to use. Call sign routing is by far more flexible and superior. - Starnet: You might consider running [Smart Group Server XL](https://github.com/F4FXL/smart-group-server-xl) from a dedicated computer instead. - Announcement: same can be achieved using transmitd. -- APRSGateway capability: I would prefer to have some sort of TCP "APRS-IS proxy" program sitting between the program and the APRS server, thus keeping the ability to directly connect to APRS-IS or not. -- Mobile APRS: Code has been ported, yet I am targeting repeaters so low priority. +- APRSGateway capability: I would prefer to have some sort of TCP "APRS-IS proxy" program sitting between the program and the APRS server, thus keeping the ability to directly connect to APRS-IS or not, depending on the system owner wish. I run mostly DStar Only repeaters, having an additional program to maintain is unnecessary burden. - DRats : No opinion on this one, I am not using it. - CallSign Server : this is a legacy from the dead project xreflector.net, I will most probably drop it. @@ -53,7 +53,7 @@ Clone the repository (only required initally) git clone https://github.com/F4FXL/DStarGateway.git cd DStarGateway ``` -## 3.2. Get latest stable version +## 3.2. Get latest stable version (recommended) From inside the cloned repository run following commands to get the latest stable version ``` git pull -p @@ -71,10 +71,19 @@ Before first time building you need to install dependencies and prerequisites ``` apt install build-essential libcurl4-openssl-dev libboost-dev ``` +If you are going to build with gpsd support, also install libgps-dev +``` +apt install libgps-dev +``` ## 3.5. Building +Regular building ``` make ``` +#### Build With GPSD Support +``` +make USE_GPS=1 +``` ## 3.6. Installing The program is meant to run as a systemd service. All bits an pieces are provided. ``` @@ -99,11 +108,12 @@ I Use [Git flow](https://danielkummer.github.io/git-flow-cheatsheet/) as my work # 5. Version History ## 5.1. Version 0.4 -- [improvement] Log enhancements ([#4])(https://github.com/F4FXL/DStarGateway/issues/4) +- [Improvement] Bring back GPSD support (https://github.com/F4FXL/DStarGateway/issues/6) +- [Improvement] Log enhancements ([#4])(https://github.com/F4FXL/DStarGateway/issues/4) ## 5.2. Version 0.3 - [Improvement] Get ride of libcongif++ dependency. When upgrading from earlier version you need to manualy delete the config file before reinstalling. ## 5.3. Version 0.2 -- [bugfix] ircDDBFreeze when repeater not found ([#1](https://github.com/F4FXL/DStarGateway/issues/1)) +- [Bugfix] ircDDBFreeze when repeater not found ([#1](https://github.com/F4FXL/DStarGateway/issues/1)) - Code sanitization ## 5.4. Version 0.1 First working version \ No newline at end of file diff --git a/Version.h b/Version.h index a785f4f..e888470 100644 --- a/Version.h +++ b/Version.h @@ -22,5 +22,10 @@ #include -const std::string VENDOR_NAME("Geoffrey Merck F4FXL / KC3FRA"); +#include "GitVersion.h" + +const std::string PRODUCT_NAME("DStarGateway"); +const std::string VENDOR_NAME("Geoffrey Merck F4FXL / KC3FRA and Contributors"); const std::string VERSION("0.4"); +const std::string FULL_PRODUCT_NAME = PRODUCT_NAME + " v" + VERSION + "-" + gitversion; +const std::string SHORT_PRODUCT_NAME = "DStarGW v" + VERSION + "-" + gitversion; diff --git a/VersionUnit.cpp b/VersionUnit.cpp index 36ea096..a382039 100644 --- a/VersionUnit.cpp +++ b/VersionUnit.cpp @@ -45,13 +45,13 @@ m_out(0U) m_data = new CAMBEData*[NUM_FRAMES]; - char vstr[32]; - snprintf(vstr, 32, "DStar GW - %s", VERSION.substr(0, 8).c_str()); + auto vstr = SHORT_PRODUCT_NAME; + vstr.resize(NUM_FRAMES, ' '); CLog::logInfo("Version text set to \"%s\"\n", vstr); CSlowDataEncoder encoder; - encoder.setTextData(std::string(vstr)); + encoder.setTextData(vstr); // Seq No and end for (unsigned int i = 0U; i < NUM_FRAMES; i++) { diff --git a/example.cfg b/example.cfg index 156def6..81c1103 100644 --- a/example.cfg +++ b/example.cfg @@ -139,6 +139,11 @@ enabled=true hostname=rotate.aprs2.net # Defaults to rotate.aprs2.net port=14580 # Defaults to 14580, there is no reason to change this password=12345 +positionSource= # Sets how the position is determined fixed or gpsd. this is ignored if DStargateway was built without GPSD support + +[GPSD] +address= # GPSD address, defaults to 127.0.0.1 +port= # GPSD port, defaults to 2947 [Log] path=/var/log/dstargateway/