Goby v2
nmea_sentence.cpp
1 // Copyright 2009-2018 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 //
6 //
7 // This file is part of the Goby Underwater Autonomy Project Libraries
8 // ("The Goby Libraries").
9 //
10 // The Goby Libraries are free software: you can redistribute them and/or modify
11 // them under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 2.1 of the License, or
13 // (at your option) any later version.
14 //
15 // The Goby Libraries are distributed in the hope that they will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
22 
23 #include <boost/algorithm/string.hpp>
24 
25 #include "goby/util/binary.h"
26 
27 #include "nmea_sentence.h"
28 
29 bool goby::util::NMEASentence::enforce_talker_length = true;
30 
31 goby::util::NMEASentence::NMEASentence(std::string s, strategy cs_strat /*= VALIDATE*/)
32 {
33  bool found_csum = false;
34  unsigned int cs;
35  // Silently drop leading/trailing whitespace if present.
36  boost::trim(s);
37  // Basic error checks ($, empty)
38  if (s.empty())
39  throw bad_nmea_sentence("NMEASentence: no message provided.");
40  if (s[0] != '$' && s[0] != '!')
41  throw bad_nmea_sentence("NMEASentence: no $ or !: '" + s + "'.");
42  // Check if the checksum exists and is correctly placed, and strip it.
43  // If it's not correctly placed, we'll interpret it as part of message.
44  // NMEA spec doesn't seem to say that * is forbidden elsewhere? (should be)
45  if (s.size() > 3 && s.at(s.size() - 3) == '*')
46  {
47  std::string hex_csum = s.substr(s.size() - 2);
48  found_csum = util::hex_string2number(hex_csum, cs);
49  s = s.substr(0, s.size() - 3);
50  }
51  // If we require a checksum and haven't found one, fail.
52  if (cs_strat == REQUIRE and !found_csum)
53  throw bad_nmea_sentence("NMEASentence: no checksum: '" + s + "'.");
54  // If we found a bad checksum and we care, fail.
55  if (found_csum && (cs_strat == REQUIRE || cs_strat == VALIDATE))
56  {
57  unsigned char calc_cs = NMEASentence::checksum(s);
58  if (calc_cs != cs)
59  throw bad_nmea_sentence("NMEASentence: bad checksum: '" + s + "'.");
60  }
61  // Split string into parts.
62  boost::split(*(std::vector<std::string>*)this, s, boost::is_any_of(","));
63  // Validate talker size.
64  if (enforce_talker_length && this->front().size() != 6)
65  throw bad_nmea_sentence("NMEASentence: bad talker length '" + s + "'.");
66 }
67 
68 unsigned char goby::util::NMEASentence::checksum(const std::string& s)
69 {
70  unsigned char csum = 0;
71 
72  if (s.empty())
73  throw bad_nmea_sentence("NMEASentence::checksum: no message provided.");
74  std::string::size_type star = s.find_first_of("*");
75  std::string::size_type dollar = s.find_first_of("$!");
76 
77  if (dollar == std::string::npos)
78  throw bad_nmea_sentence("NMEASentence::checksum: no $ or ! found.");
79 
80  if (star == std::string::npos)
81  star = s.length();
82 
83  for (std::string::size_type i = dollar + 1; i < star; ++i) csum ^= s[i];
84  return csum;
85 }
86 
87 std::string goby::util::NMEASentence::message_no_cs() const
88 {
89  std::string message = "";
90 
91  for (const_iterator it = begin(), n = end(); it < n; ++it) message += *it + ",";
92 
93  // kill last ","
94  message.resize(message.size() - 1);
95  return message;
96 }
97 
98 std::string goby::util::NMEASentence::message() const
99 {
100  std::string bare = message_no_cs();
101  std::stringstream message;
102  unsigned char csum = NMEASentence::checksum(bare);
103  message << bare << "*";
104  message << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << unsigned(csum);
105  return message.str();
106 }