#include "default_sentence_parser.h" #include #include using namespace AIS; /* Called to find NMEA start (scan past any headers, META data, etc.; returns NMEA payload) This implementation will scan past META data that start and end with a '\'. It will also stop at NMEA CRC. */ StringRef DefaultSentenceParser::onScanForNmea(const StringRef &_strSentence) const { const char *pPayloadStart = _strSentence.data(); size_t uPayloadSize = _strSentence.size(); // check for common META data block headers const char *pCh = pPayloadStart; if (*pCh == '\\') { // find META data block end pCh = (const char*)memchr(pCh + 1, '\\', uPayloadSize - 1); if (pCh != nullptr) { pPayloadStart = pCh + 1; uPayloadSize = _strSentence.size() - (pPayloadStart - _strSentence.data()); } else { uPayloadSize = 0; } } else if (std::strncmp(pCh, "$P", 2) == 0) { uPayloadSize = 0; } // find payload size (using crc '*' plus 2 chars for crc value) if (uPayloadSize > 0) { pCh = (const char*)memchr(pPayloadStart, '*', uPayloadSize); if (pCh != nullptr) { uPayloadSize = pCh + 3 - pPayloadStart; } else { uPayloadSize = 0; } } return StringRef(pPayloadStart, uPayloadSize); } /* calc header string from original line and extracted NMEA payload */ StringRef DefaultSentenceParser::getHeader(const StringRef &_strLine, const StringRef &_strNmea) const { StringRef strHeader(_strLine.data(), 0); if (_strNmea.data() > _strLine.data()) { strHeader = _strLine.substr(0, _strNmea.data() - _strLine.data()); // remove last '\' if ( (strHeader.empty() == false) && (strHeader[strHeader.size() - 1] == '\\') ) { strHeader.remove_suffix(1); } // remove first '\\' if ( (strHeader.empty() == false) && (strHeader[0] == '\\') ) { strHeader.remove_prefix(1); } } return strHeader; } /* calc footer string from original line and extracted NMEA payload */ StringRef DefaultSentenceParser::getFooter(const StringRef &_strLine, const StringRef &_strNmea) const { StringRef strFooter(_strLine.data(), 0); const char *pLineEnd = _strLine.data() + _strLine.size(); const char *pNmeaEnd = _strNmea.data() + _strNmea.size(); if (pLineEnd > pNmeaEnd) { // NOTE: '_strLine' will end with or strFooter = StringRef(pNmeaEnd, pLineEnd - pNmeaEnd - 1); // remove last '' if ( (strFooter.empty() == false) && (strFooter[strFooter.size() - 1] == '\r') ) { strFooter.remove_suffix(1); } // remove first ',' if ( (strFooter.empty() == false) && (strFooter[0] == ',') ) { strFooter.remove_prefix(1); } } return strFooter; } /* extracts the timestamp from the meta info */ uint64_t DefaultSentenceParser::getTimestamp(const AIS::StringRef &_strHeader, const AIS::StringRef &_strFooter) const { uint64_t uTimestamp = 0; // try to get timestamp from header // NOTE: assumes header has comma seperated fields with 'c:' identifying unix timestamp if (_strHeader.size() > 0) { // seperate header into words std::array words; size_t n = AIS::seperate<','>(words, _strHeader); // find timestamp for (size_t i = 0; i < n; i++) { const auto &word = words[i]; if ( (word.empty() == false) && (word[0] == 'c') ) { uTimestamp = (uint64_t)std::strtoul(word.data()+2, nullptr, 10); } } } // try to get timestamp from footer // NOTE: assumes footer first word as timestamp if ( (_strFooter.empty() == false) && (uTimestamp == 0) ) { uTimestamp = (uint64_t)std::strtoul(_strFooter.data()+1, nullptr, 10); } return uTimestamp; }