diff --git a/APRSCollector.cpp b/APRSCollector.cpp index 92284e0..9f10a18 100644 --- a/APRSCollector.cpp +++ b/APRSCollector.cpp @@ -27,6 +27,7 @@ #include "Utils.h" #include "NMEASentenceCollector.h" #include "GPSACollector.h" +#include "RSMS1AMessageCollector.h" const unsigned int APRS_CSUM_LENGTH = 4U; const unsigned int APRS_DATA_LENGTH = 300U; @@ -38,6 +39,7 @@ const char APRS_SYMBOL = 'K'; CAPRSCollector::CAPRSCollector() : m_collectors() { + m_collectors.push_back(new CRSMS1AMessageCollector()); m_collectors.push_back(new CGPSACollector()); m_collectors.push_back(new CNMEASentenceCollector("$GPRMC")); m_collectors.push_back(new CNMEASentenceCollector("$GPGGA")); diff --git a/APRSUtils.cpp b/APRSUtils.cpp new file mode 100644 index 0000000..3d0839d --- /dev/null +++ b/APRSUtils.cpp @@ -0,0 +1,34 @@ +/* + * 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 "APRSUtils.h" + +void CAPRSUtils::dstarCallsignToAPRS(std::string& dstarCallsign) +{ + if(dstarCallsign[dstarCallsign.length() - 1] == ' ') { + boost::trim(dstarCallsign); + } else { + //loop until got rid of all double blanks + while(dstarCallsign.find(" ") != std::string::npos) { + boost::replace_all(dstarCallsign, " ", " "); + } + boost::replace_all(dstarCallsign, " ", "-");//replace remaining blank with a - + } +} \ No newline at end of file diff --git a/APRSUtils.h b/APRSUtils.h new file mode 100644 index 0000000..2ad53d3 --- /dev/null +++ b/APRSUtils.h @@ -0,0 +1,27 @@ +/* + * 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 + +class CAPRSUtils +{ +public: + static void dstarCallsignToAPRS(std::string& dstarCallsign); +}; \ No newline at end of file diff --git a/NMEASentenceCollector.cpp b/NMEASentenceCollector.cpp index 63d2087..2685625 100644 --- a/NMEASentenceCollector.cpp +++ b/NMEASentenceCollector.cpp @@ -24,6 +24,7 @@ #include "NMEASentenceCollector.h" #include "StringUtils.h" #include "Log.h" +#include "APRSUtils.h" CNMEASentenceCollector::CNMEASentenceCollector(const std::string& sentence) : CSentenceCollector(SLOW_DATA_TYPE_GPS, sentence, '\x0A') @@ -82,7 +83,7 @@ unsigned int CNMEASentenceCollector::getDataInt(unsigned char * data, unsigned i fixUpNMEATimeStamp(nmea); std::string fromCall = getMyCall(); - dstarCallsignToAPRS(fromCall); + CAPRSUtils::dstarCallsignToAPRS(fromCall); std::string aprsFrame(fromCall); aprsFrame.append("-5>GPS30,DSTAR*:") .append(nmea) @@ -101,20 +102,6 @@ unsigned int CNMEASentenceCollector::getDataInt(unsigned char * data, unsigned i return aprsFrameLen; } - -void CNMEASentenceCollector::dstarCallsignToAPRS(std::string& dstarCallsign) -{ - if(dstarCallsign[dstarCallsign.length() - 1] == ' ') { - boost::trim(dstarCallsign); - } else { - //loop until got rid of all double blanks - while(dstarCallsign.find(" ") != std::string::npos) { - boost::replace_all(dstarCallsign, " ", " "); - } - boost::replace_all(dstarCallsign, " ", "-");//replace remaining blank with a - - } -} - // When set on manual position Icom radios send 000000.00 as NMEA timestamps // this is a dirty hack to correct this issue. Actually I am not sure about APRS // software being peeky about this except APRS.fi diff --git a/RSMS1AMessageCollector.cpp b/RSMS1AMessageCollector.cpp new file mode 100644 index 0000000..398cc47 --- /dev/null +++ b/RSMS1AMessageCollector.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2010,2012,2018 by Jonathan Naylor G4KLX + * 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 + +#include "RSMS1AMessageCollector.h" +#include "StringUtils.h" +#include "Log.h" +#include "Utils.h" +#include "APRSUtils.h" + +const unsigned int APRS_CSUM_LENGTH = 4U; + +CRSMS1AMessageCollector::CRSMS1AMessageCollector() : +CSentenceCollector(SLOW_DATA_TYPE_GPS, "$$Msg", '\x0D') +{ + +} + +bool CRSMS1AMessageCollector::isValidSentence(const std::string& sentence) +{ + return isValidGPSA(sentence); +} + +bool CRSMS1AMessageCollector::isValidGPSA(const std::string& msg) +{ + if(msg.empty() || !boost::starts_with(msg, "$$Msg")) + return false; + + std::vector splits; + boost::split(splits, msg, boost::is_any_of(",")); + + bool ret = splits.size() == 4 + && !splits[1].empty() + && !splits[2].empty() + && splits[3].length() > 6U; + return ret; + + // CUtils::dump("RS-MS1A:", (unsigned char *)gpsa.c_str(), gpsa.length() + 1U); + // CLog::logDebug("RS-MS1A: %s", gpsa.c_str()); + + // auto thirdCommaPos = CStringUtils::find_nth(gpsa, 0U, ',', 3); + // auto csum = calcCRC(gpsa, thirdCommaPos + 6 + 1, gpsa.length() - thirdCommaPos - 2U - 6U); + // auto csumStr = CStringUtils::string_format("%06X", csum); + // CLog::logDebug("RS-MS1A CRC: %s", csumStr.c_str()); + + // auto expectedCsum = gpsa.substr(5U, APRS_CSUM_LENGTH); + // bool res = ::strcasecmp(csumStr.c_str(), expectedCsum.c_str()) == 0; + // return res; +} + +unsigned int CRSMS1AMessageCollector::calcCRC(const std::string& gpsa, unsigned int start, unsigned int length) +{ + unsigned int icomcrc = 0xFFFFU; + auto end = start + length; + if(end > gpsa.length()) { + end = gpsa.length(); + } + + for (unsigned int j = start; j < end; j++) { + unsigned char ch = (unsigned char)gpsa[j]; + + for (unsigned int i = 0U; i < 8U; i++) { + bool xorflag = (((icomcrc ^ ch) & 0x01U) == 0x01U); + + icomcrc >>= 1; + + if (xorflag) + icomcrc ^= 0x8408U; + + ch >>= 1; + } + } + + return ~icomcrc & 0xFFFFU; +} + +unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned int length) +{ + if(data == nullptr || length == 0U || getSentence().empty()) + return 0U; + + auto sentence = getSentence(); + + std::vector splits; + boost::split(splits, sentence, boost::is_any_of(",")); + + bool ret = splits.size() == 4 + && !splits[1].empty() + && !splits[2].empty() + && splits[3].length() > 6U; + if(!ret) { + return 0U; + } + + auto sender = splits[1]; + auto recipient = CUtils::ToUpper(splits[2]); + auto message = splits[3].substr(6, splits[3].length() - 2); + + CAPRSUtils::dstarCallsignToAPRS(sender); + CAPRSUtils::dstarCallsignToAPRS(recipient); + recipient.resize(9, ' '); + + //unescape commas in message body + boost::replace_all(message, "o,", ","); + + auto aprsFrame = CStringUtils::string_format("%s-5>APDPRS,DSTAR*::%s:%s\r\n", sender.c_str(), recipient.c_str(), message.c_str()); + + auto aprsFrameLen = aprsFrame.length(); + + if(length < aprsFrameLen) { + CLog::logDebug("Not enough space to copy GPS-A APRS frame"); + return 0U; + } + + for(unsigned int i = 0U; i < aprsFrameLen; i++){ + data[i] = aprsFrame[i]; + } + + return aprsFrameLen; +} \ No newline at end of file diff --git a/RSMS1AMessageCollector.h b/RSMS1AMessageCollector.h new file mode 100644 index 0000000..359eaab --- /dev/null +++ b/RSMS1AMessageCollector.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010,2012,2018 by Jonathan Naylor G4KLX + * 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 "SentenceCollector.h" + +class CRSMS1AMessageCollector : public CSentenceCollector +{ +public: + CRSMS1AMessageCollector(); + +protected: + unsigned int getDataInt(unsigned char * data, unsigned int length); + bool isValidSentence(const std::string& sentence); + +private: + static unsigned int calcCRC(const std::string& gpsa, unsigned int start, unsigned int length); + static bool isValidGPSA(const std::string& gpsa); + + std::string m_sentence; + std::string m_collector; + +}; \ No newline at end of file diff --git a/SlowDataCollector.cpp b/SlowDataCollector.cpp index f23aa5c..b651ae7 100644 --- a/SlowDataCollector.cpp +++ b/SlowDataCollector.cpp @@ -21,6 +21,7 @@ #include #include "SlowDataCollector.h" +#include "Log.h" const unsigned int SLOW_DATA_BLOCK_LENGTH = 6U; @@ -68,6 +69,24 @@ bool CSlowDataCollector::writeData(const unsigned char* data) break; } + // unsigned char rxDataType = (m_buffer[0] & SLOW_DATA_TYPE_MASK); + + // switch (rxDataType) + // { + // case SLOW_DATA_TYPE_MASK: CLog::logDebug("SLOW_DATA_TYPE_MASK "); break; + // case SLOW_DATA_TYPE_GPS: CLog::logDebug("SLOW_DATA_TYPE_GPS "); break; + // case SLOW_DATA_TYPE_TEXT: CLog::logDebug("SLOW_DATA_TYPE_TEXT "); break; + // case SLOW_DATA_TYPE_HEADER: CLog::logDebug("SLOW_DATA_TYPE_HEADER "); break; + // case SLOW_DATA_TYPE_MESSAGE: CLog::logDebug("SLOW_DATA_TYPE_MESSAGE "); break; + // case SLOW_DATA_TYPE_FAST_DATA1: CLog::logDebug("SLOW_DATA_TYPE_FAST_DATA1 "); break; + // case SLOW_DATA_TYPE_FAST_DATA2: CLog::logDebug("SLOW_DATA_TYPE_FAST_DATA2 "); break; + // case SLOW_DATA_TYPE_SQUELCH: CLog::logDebug("SLOW_DATA_TYPE_SQUELCH "); break; + // case SLOW_DATA_LENGTH_MASK: CLog::logDebug("SLOW_DATA_LENGTH_MASK "); break; + // default: + // CLog::logDebug("!!!!!!!!!!!!!!! %X", rxDataType); + // break; + // }; + if((m_buffer[0] & SLOW_DATA_TYPE_MASK) == m_slowDataType) return addData(m_buffer + 1U);