• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fliuzzi02 / nmealib / 23093480989

14 Mar 2026 06:10PM UTC coverage: 94.299% (+1.7%) from 92.6%
23093480989

push

github

web-flow
PlatformIO Integration (#72)

* [UPDATE] Replace GPL with MIT License in LICENSE file

* [REFINE] Remove hasEqualContent method from NMEA 0183 message classes and update related tests

* [UPDATE] Enhance README with PlatformIO integration details and supported protocols

* Refactor NMEA message creation to improve error handling

- Updated MTW, MWV, RMC, VHW, VTG, VWR, ZDA, and NMEA2000 message creation methods to use NMEALIB_RETURN_ERROR for exception handling.
- Enhanced parsing logic for optional fields using detail::parseOptionalDouble and similar functions to streamline error checking.
- Improved readability and maintainability by removing try-catch blocks in favor of direct error handling.
- Added checks for base message validity in Nmea0183Factory and Nmea2000Factory to ensure robust message creation.

* [FIX] Data type for cross platform use

* [UPDATE] Implement version update script for automated release process

* [UPDATE] Update nmealib version in installation instructions for PlatformIO integration

* [REFINE] Remove unnecessary tolerance calculation in fromValue method

* [UPDATE] Adjust default parallel build level and enhance compiler warning settings

* [ADD] Integrate PlatformIO configuration and example usage for NMEA library

* [UPDATE] Refactor static analysis problems

268 of 305 new or added lines in 21 files covered. (87.87%)

1 existing line in 1 file now uncovered.

1836 of 1947 relevant lines covered (94.3%)

25.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.55
/src/nmea0183.cpp
1
#include "nmealib/nmea0183.h"
2

3
#include "nmealib/detail/errorSupport.h"
4
#include "nmealib/detail/parse.h"
5

6
#include <sstream>
7
#include <iomanip>
8
#include <algorithm>
9
#include <cctype>
10
#include <cstdlib>
11
#include <numeric>
12
#include <cmath>
13

14
namespace nmealib {
15
namespace nmea0183 {
16

17
Message0183::Message0183(std::string raw,
104✔
18
                        TimePoint ts,
19
                        char startChar,
20
                        std::string talker,
21
                        std::string sentenceType,
22
                        std::string payload) noexcept
104✔
23
    : Message(std::move(raw), Type::NMEA0183, ts),
24
    startChar_(startChar), 
104✔
25
    talker_(std::move(talker)), 
104✔
26
    sentenceType_(std::move(sentenceType)), 
104✔
27
    payload_(std::move(payload)) {
312✔
28
    calculatedChecksumStr_ = computeChecksum(payload_);
104✔
29
}
104✔
30

31
Message0183::Message0183(std::string raw,
33✔
32
                        TimePoint ts,
33
                        char startChar,
34
                        std::string talker,
35
                        std::string sentenceType,
36
                        std::string payload,
37
                        std::string checksumStr) noexcept
33✔
38
    : Message(std::move(raw), Type::NMEA0183, ts),
39
    startChar_(startChar), 
33✔
40
    talker_(std::move(talker)), 
33✔
41
    sentenceType_(std::move(sentenceType)), 
33✔
42
    payload_(std::move(payload)),
33✔
43
    checksumStr_(std::move(checksumStr)) {
99✔
44
    calculatedChecksumStr_ = computeChecksum(payload_);
33✔
45
}
33✔
46

47
std::unique_ptr<Message0183> Message0183::create(const std::string& raw, TimePoint ts) {
139✔
48
    std::string context = "Message0183::create";
139✔
49
#if defined(NMEALIB_NO_EXCEPTIONS)
50
    if (!validateFormat(context, raw)) {
51
        return nullptr;
52
    }
53
#else
54
    validateFormat(context, raw);
139✔
55
#endif
56

57
    char startChar = raw[0];
137✔
58

59
    bool hasCRLF = raw.size() >= 2 && raw.substr(raw.size() - 2) == "\r\n";
139✔
60

61
    // Extract talker and sentence type from the raw sentence.
62
    std::string talker = raw.substr(1, 2);
137✔
63
    std::string sentenceType = raw.substr(3, 3);
137✔
64
    bool hasChecksum = raw.find('*') != std::string::npos;
137✔
65
    if(hasChecksum) {
137✔
66
        size_t asteriskPos = raw.find('*');
67
        std::string payload = raw.substr(1, asteriskPos - 1); // Exclude start char and checksum part
33✔
68
        std::string checksumStr = raw.substr(asteriskPos + 1, 2);
33✔
69
        return std::unique_ptr<Message0183>(new Message0183(raw, ts, startChar, talker, sentenceType, payload, checksumStr));
132✔
70
    } else {
71
        if (hasCRLF) {
104✔
72
            std::string payload = raw.substr(1, raw.size() - 3); // Exclude start char and CRLF
104✔
73
            return std::unique_ptr<Message0183>(new Message0183(raw, ts, startChar, talker, sentenceType, payload));
416✔
74
        } else {
75
            std::string payload = raw.substr(1); // Exclude start char only
×
76
            return std::unique_ptr<Message0183>(new Message0183(raw, ts, startChar, talker, sentenceType, payload));
×
77
        }
78
    }
79
}
80

81
bool Message0183::validateFormat(const std::string& context, const std::string& raw) {
139✔
82
    // TODO: I have to check that it correspons to the minimum sentence: $XXXXX*ZZ<CR><LF>
83
    // and also without checksum: $XXXXX<CR><LF>
84
    // and also without CRLF: $XXXXX*ZZ and $XXXXX
85
    if(raw.size() > 82) {
139✔
86
        NMEALIB_RETURN_ERROR_VALUE(TooLongSentenceException(context, "Input string length: " + std::to_string(raw.size())), false);
3✔
87
    }
88
    if(raw.empty() || (raw[0] != '$' && raw[0] != '!')) {
138✔
89
        NMEALIB_RETURN_ERROR_VALUE(InvalidStartCharacterException(context), false);
2✔
90
    }
91

92
    bool hasChecksum = raw.find('*') != std::string::npos;
137✔
93
    if (hasChecksum) {
137✔
94
        // If it has a checksum, find the position of '*' and extract payload and checksum accordingly.
95
        size_t asteriskPos = raw.find('*');
96
        std::string checksumStr = raw.substr(asteriskPos + 1, 2);
33✔
97
        if(!isHexByte(checksumStr)) {
33✔
NEW
98
            NMEALIB_RETURN_ERROR_VALUE(NoChecksumException(context, "Invalid checksum format: " + checksumStr), false);
×
99
        }
100
    }
101

102
    return true;
137✔
103
}
104

105
std::unique_ptr<nmealib::Message> Message0183::clone() const {
×
106
    return std::unique_ptr<Message0183>(new Message0183(*this));
×
107
}
108

109
std::string Message0183::getPayload() const noexcept {
86✔
110
    return payload_;
86✔
111
}
112

113
char Message0183::getStartChar() const noexcept {
1✔
114
    return startChar_;
1✔
115
}
116

117
std::string Message0183::getTalker() const noexcept {
32✔
118
    return talker_;
32✔
119
}
120

121
std::string Message0183::getSentenceType() const noexcept {
202✔
122
    return sentenceType_;
202✔
123
}
124

125
std::string Message0183::getChecksumStr() const {
3✔
126
    if (checksumStr_.empty()) {
3✔
127
        NMEALIB_RETURN_ERROR_VALUE(NoChecksumException("Message0183::getChecksumStr", "This sentence does not contain a checksum"), std::string());
2✔
128
    }
129
    return checksumStr_;
2✔
130
}
131

132
std::string Message0183::getCalculatedChecksumStr() const noexcept {
2✔
133
    return calculatedChecksumStr_;
2✔
134
}
135

136
std::string Message0183::getStringContent(bool verbose) const noexcept {
×
137
    std::stringstream ss;
×
138

139
    ss << this->toString(verbose);
×
140

141
    if (verbose) ss << "\tUnimplemented sentence type";
×
142
    else ss << "Unimplemented sentence type";
×
143

144
    return ss.str();
×
145
}
×
146

147
std::string Message0183::toString(bool verbose) const noexcept {
24✔
148
    std::stringstream ss;
24✔
149
    std::string validity = "KO";
24✔
150
    if(validate()) {
24✔
151
        validity = "OK";
152
    }
153

154
    if (verbose) {
24✔
155
        ss << "--------------------------------\n";
12✔
156
        ss << "Protocol: " << typeToString(type_) << "\n";
36✔
157
        ss << "Talker: " << getTalker() << "\n";
24✔
158
        ss << "Sentence Type: " << getSentenceType() << "\n";
24✔
159
        ss << "Checksum: " << (checksumStr_.empty() ? "None" : validity) << "\n";
24✔
160
        ss << "Fields:\n";
12✔
161
    } else {
162
        ss << "[" << validity << "] " << typeToString(type_) << " " << getTalker() << " " << getSentenceType() << ": ";
36✔
163
    }
164

165
    return ss.str();
24✔
166
}
24✔
167

168
std::string Message0183::serialize() const {
26✔
169
    return rawData_;
26✔
170
}
171

172
bool Message0183::validate() const noexcept{
27✔
173
    if (checksumStr_.empty()) {
27✔
174
        return true;
175
    }
176

177
    return checksumStr_ == getCalculatedChecksumStr();
2✔
178
}
179

180
std::string Message0183::computeChecksum(const std::string& payload) noexcept {
137✔
181
    uint8_t checksum = std::accumulate(payload.begin(), payload.end(), static_cast<uint8_t>(0),
182
        [](uint8_t acc, char c) { return acc ^ static_cast<uint8_t>(c); });
4,981✔
183
    std::stringstream ss;
137✔
184
    ss << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(checksum);
137✔
185
    return ss.str();
137✔
186
}
137✔
187

188
bool Message0183::isHexByte(const std::string& s) noexcept {
33✔
189
    if (s.size() != 2) return false;
33✔
190
    return std::isxdigit(static_cast<unsigned char>(s[0])) &&
33✔
191
           std::isxdigit(static_cast<unsigned char>(s[1]));
33✔
192
}
193

UNCOV
194
double Message0183::convertNmeaCoordinateToDecimalDegrees(const std::string& nmeaCoordinate) {
×
NEW
195
    double converted = 0.0;
×
NEW
196
    if (!detail::parseNmeaCoordinate(nmeaCoordinate, converted)) {
×
NEW
197
        NMEALIB_RETURN_ERROR_VALUE(NmeaException("Message0183::convertNmeaCoordinateToDecimalDegrees", "Invalid coordinate format", nmeaCoordinate), 0.0);
×
198
    }
199

NEW
200
    return converted;
×
201
}
202

203
// TODO: Re-expose this method to all child classes and restrict the inherited ones like i did in 
204
bool Message0183::operator==(const Message0183& other) const noexcept {
13✔
205
    return startChar_ == other.startChar_ &&
26✔
206
           talker_ == other.talker_ &&
13✔
207
           sentenceType_ == other.sentenceType_ &&
13✔
208
           payload_ == other.payload_ &&
13✔
209
           checksumStr_ == other.checksumStr_ &&
13✔
210
           calculatedChecksumStr_ == other.calculatedChecksumStr_ &&
39✔
211
           Message::operator==(other);
13✔
212
}
213

214
} // namespace nmea0183
215
} // namespace nmealib
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc