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

fliuzzi02 / nmealib / 24076065561

07 Apr 2026 10:10AM UTC coverage: 91.728% (-0.04%) from 91.77%
24076065561

push

github

web-flow
Fix/unrecognized nmea0183 sentencies (#97)

* Update Protocol Support Matrix with additional NMEA0183 message entries

* Update Protocol Support Matrix to include hyperlinks for NMEA0183 messages

* Update README.md to enhance NMEA 0183 and NMEA 2000 protocol support tables

* Update README.md to reflect new directory structure and additional components

* Enhance GLL, RMA, and RMC sentence parsing to accept optional fields and improve error handling

* Enhance RMC parsing to support decimal UTC and handle empty magnetic variation fields

17 of 19 new or added lines in 3 files covered. (89.47%)

2883 of 3143 relevant lines covered (91.73%)

23.7 hits per line

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

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

3
#include "nmealib/detail/errorSupport.h"
4
#include "nmealib/detail/parse.h"
5
#include <cmath>
6
#include <iomanip>
7
#include <limits>
8
#include <sstream>
9
#include <vector>
10

11
#include "nmealib/nmea0183/nmea0183Factory.h"
12

13
namespace nmealib {
14
namespace nmea0183 {
15

16
std::unique_ptr<RMC> RMC::create(std::unique_ptr<Message0183> baseMessage) {
12✔
17
    std::string context = "RMC::create";
12✔
18
    if (baseMessage->getSentenceType() != "RMC") {
12✔
19
        NMEALIB_RETURN_ERROR(NotRMCException(context, "Expected sentence type 'RMC', got " + baseMessage->getSentenceType()));
×
20
    }
21

22
    // Parse the payload to extract RMC-specific fields
23
    std::string payload = baseMessage->getPayload();
12✔
24
    std::istringstream ss(payload);
12✔
25
    std::string token;
26
    std::vector<std::string> fields;
12✔
27

28
    while (std::getline(ss, token, ',')) {
166✔
29
        fields.push_back(token);
154✔
30
    }
31

32
    // Drop first element which is the sentence type (e.g. "GPRMC")
33
    if (!fields.empty()) {
12✔
34
        fields.erase(fields.begin());
35
    }
36

37
    size_t messageSize = fields.size();
38
    if (messageSize != 11 && messageSize != 12 && messageSize != 13) {
12✔
39
        NMEALIB_RETURN_ERROR(NotRMCException(context, "Insufficient fields in RMC payload: expected 11, 12 or 13, got " + std::to_string(fields.size()) + ". Payload: " + payload));
3✔
40
    }
41

42
    unsigned int utcFix = 0U;
11✔
43
    double latitude = 0.0;
11✔
44
    double longitude = 0.0;
11✔
45
    double speedOverGround = 0.0;
11✔
46
    double courseOverGround = 0.0;
11✔
47
    unsigned int date = 0U;
11✔
48
    double magneticVariation = 0.0;
11✔
49

50
    auto parseUtcFix = [](const std::string& utcField, unsigned int& outUtcFix) {
11✔
51
        if (utcField.empty()) {
11✔
NEW
52
            outUtcFix = 0U;
×
NEW
53
            return true;
×
54
        }
55

56
        double utcDouble = 0.0;
11✔
57
        if (!detail::parseOptionalDouble(utcField, utcDouble) || utcDouble < 0.0 ||
11✔
58
            utcDouble > static_cast<double>(std::numeric_limits<unsigned int>::max())) {
59
            return false;
60
        }
61

62
        outUtcFix = static_cast<unsigned int>(utcDouble);
11✔
63
        return true;
11✔
64
    };
65

66
    if (!parseUtcFix(fields[0], utcFix) ||
22✔
67
        !detail::parseNmeaCoordinate(fields[2], latitude) ||
22✔
68
        !detail::parseNmeaCoordinate(fields[4], longitude) ||
22✔
69
        !detail::parseOptionalDouble(fields[6], speedOverGround) ||
10✔
70
        !detail::parseOptionalDouble(fields[7], courseOverGround) ||
10✔
71
        !detail::parseOptionalUnsigned(fields[8], date) ||
20✔
72
        !detail::parseOptionalDouble(fields[9], magneticVariation)) {
73
        NMEALIB_RETURN_ERROR(NmeaException(context, "Error parsing RMC fields"));
×
74
    }
75

76
    char status = fields[1].empty() ? '\0' : fields[1][0];
11✔
77
    char latDirection = fields[3].empty() ? '\0' : fields[3][0];
11✔
78
    char lonDirection = fields[5].empty() ? '\0' : fields[5][0];
11✔
79
    char magneticVariationDirection = fields[10].empty() ? '\0' : fields[10][0];
11✔
80
    char modeIndicator = '\0';
81
    char navigationStatus = '\0';
82

83
    if (messageSize >= 12) {
11✔
84
        modeIndicator = fields[11].empty() ? '\0' : fields[11][0];
10✔
85
    }
86

87
    if (messageSize == 13) {
11✔
88
        navigationStatus = fields[12].empty() ? '\0' : fields[12][0];
9✔
89
    }
90

91
    return std::unique_ptr<RMC>(new RMC(std::move(*baseMessage), utcFix, status, latitude, latDirection, longitude, lonDirection, speedOverGround, courseOverGround, date, magneticVariation, magneticVariationDirection, modeIndicator, navigationStatus));
33✔
92
}
24✔
93

94
RMC::RMC(Message0183 baseMessage, 
×
95
         unsigned int utcFix, 
96
         char status, 
97
         double latitude,
98
         char latitudeDirection, 
99
         double longitude, 
100
         char longitudeDirection,
101
         double speedOverGround, 
102
         double courseOverGround, 
103
         unsigned int date, 
104
         double magneticVariation,
105
         char magneticVariationDirection,
106
         char modeIndicator,
107
         char navigationStatus) noexcept
11✔
108
    : Message0183(std::move(baseMessage)), 
109
      utcFix_(utcFix), 
11✔
110
      status_(status), 
11✔
111
      latitude_(latitude), 
11✔
112
      latitudeDirection_(latitudeDirection),
11✔
113
      longitude_(longitude), 
11✔
114
      longitudeDirection_(longitudeDirection),
11✔
115
      speedOverGround_(speedOverGround), 
11✔
116
      courseOverGround_(courseOverGround), 
11✔
117
      date_(date), 
11✔
118
      magneticVariation_(magneticVariation),
11✔
119
      magneticVariationDirection_(magneticVariationDirection),
11✔
120
      modeIndicator_(modeIndicator),
11✔
121
      navigationStatus_(navigationStatus) {}
11✔
122

123
RMC::RMC(std::string talkerId, unsigned int utcFix, 
5✔
124
        char status, 
125
        double latitude,
126
        char latitudeDirection, 
127
        double longitude,
128
        char longitudeDirection,
129
        double speedOverGround, 
130
        double courseOverGround, 
131
        unsigned int date, 
132
        double magneticVariation,
133
        char magneticVariationDirection,
134
        char modeIndicator,
135
        char navigationStatus) : 
5✔
136
        Message0183(*Message0183::create(composeRaw(talkerId, utcFix, status, latitude, 
5✔
137
                                latitudeDirection, longitude, longitudeDirection, 
138
                                speedOverGround, courseOverGround, date, magneticVariation, 
139
                                magneticVariationDirection, modeIndicator, navigationStatus))),
140
        utcFix_(utcFix),
5✔
141
        status_(status),
5✔
142
        latitude_(latitude),
5✔
143
        latitudeDirection_(latitudeDirection),
5✔
144
        longitude_(longitude),
5✔
145
        longitudeDirection_(longitudeDirection),
5✔
146
        speedOverGround_(speedOverGround),
5✔
147
        courseOverGround_(courseOverGround),
5✔
148
        date_(date),
5✔
149
        magneticVariation_(magneticVariation),
5✔
150
        magneticVariationDirection_(magneticVariationDirection),
5✔
151
        modeIndicator_(modeIndicator),
5✔
152
        navigationStatus_(navigationStatus) {}
15✔
153

154
std::unique_ptr<nmealib::Message> RMC::clone() const {
1✔
155
    return std::unique_ptr<RMC>(new RMC(*this));
1✔
156
}
157

158
std::string RMC::getStringContent(bool verbose) const noexcept {
2✔
159
    std::ostringstream ss;
2✔
160
    ss << this->toString(verbose);
2✔
161
    std::ostringstream latStream;
2✔
162
    latStream << std::setprecision(10) << latitude_;
2✔
163
    const std::string latitudeStr = latStream.str();
164

165
    std::ostringstream lonStream;
2✔
166
    lonStream << std::setprecision(10) << longitude_;
2✔
167
    const std::string longitudeStr = lonStream.str();
168

169
    if (verbose) {
2✔
170
        ss << "\tUTC Fix: " << utcFix_ << "\n";
1✔
171
        ss << "\tStatus: " << status_ << "\n";
1✔
172
        ss << "\tLatitude: " << latitudeStr << " " << latitudeDirection_ << "\n";
1✔
173
        ss << "\tLongitude: " << longitudeStr << " " << longitudeDirection_ << "\n";
1✔
174
        ss << "\tSpeed Over Ground: " << speedOverGround_ << "\n";
1✔
175
        ss << "\tCourse Over Ground: " << courseOverGround_ << "\n";
1✔
176
        ss << "\tDate: " << date_ << "\n";
1✔
177
        ss << "\tMagnetic Variation: " << magneticVariation_ << " " << magneticVariationDirection_ << "\n";
2✔
178
        ss << "\tMode Indicator: " << modeIndicator_ << "\n";
1✔
179
        ss << "\tNavigation Status: " << navigationStatus_;
1✔
180
        ss << "\n";
1✔
181
    } else {
182
        ss << "UTC Fix=" << utcFix_
1✔
183
           << ", Status=" << status_
1✔
184
           << ", Lat=" << latitudeStr << latitudeDirection_
1✔
185
           << ", Lon=" << longitudeStr << longitudeDirection_
1✔
186
           << ", SOG=" << speedOverGround_
4✔
187
           << ", COG=" << courseOverGround_
1✔
188
           << ", Date=" << date_
1✔
189
           << ", MagVar=" << magneticVariation_ << magneticVariationDirection_
1✔
190
           << ", Mode=" << modeIndicator_
1✔
191
           << ", NavStatus=" << navigationStatus_;
3✔
192
    }
193
    return ss.str();
2✔
194
}
4✔
195

196
std::string RMC::composeRaw(const std::string& talkerId, unsigned int utcFix, 
5✔
197
                            char status, 
198
                            double latitude,
199
                            char latitudeDirection, 
200
                            double longitude,
201
                            char longitudeDirection,
202
                            double speedOverGround, 
203
                            double courseOverGround, 
204
                            unsigned int date, 
205
                            double magneticVariation,
206
                            char magneticVariationDirection,
207
                            char modeIndicator,
208
                            char navigationStatus) {
209
    std::ostringstream payloadStream;
5✔
210
    payloadStream << talkerId << "RMC,";
5✔
211
    payloadStream << std::setw(6) << std::setfill('0') << utcFix << ",";
5✔
212
    payloadStream << status << ",";
5✔
213
    double latitudeDegrees = std::floor(latitude);
5✔
214
    double latitudeMinutes = (latitude - latitudeDegrees) * 60.0;
5✔
215
    latitude = latitudeDegrees * 100.0 + latitudeMinutes; // Convert from decimal degrees to ddmm.mmmm
5✔
216
    double longitudeDegrees = std::floor(longitude);
5✔
217
    double longitudeMinutes = (longitude - longitudeDegrees) * 60.0;
5✔
218
    longitude = longitudeDegrees * 100.0 + longitudeMinutes; // Convert from decimal degrees to dddmm.mmmm
5✔
219
    payloadStream << std::fixed << std::setprecision(4) << latitude << ",";
5✔
220
    payloadStream << latitudeDirection << ",";
5✔
221
    payloadStream << std::fixed << std::setprecision(4) << longitude << ",";
5✔
222
    payloadStream << longitudeDirection << ",";
5✔
223
    payloadStream << std::fixed << std::setprecision(1) << speedOverGround << ",";
5✔
224
    payloadStream << std::fixed << std::setprecision(1) << courseOverGround << ",";
5✔
225
    payloadStream << std::setw(6) << std::setfill('0') << date << ",";
5✔
226
    payloadStream << std::fixed << std::setprecision(1) << magneticVariation << ",";
5✔
227
    payloadStream << magneticVariationDirection << ",";
5✔
228
    payloadStream << modeIndicator << ",";
5✔
229
    payloadStream << navigationStatus;
5✔
230

231
    std::string payload = payloadStream.str();
232
    return "$" + payload + "\r\n";
15✔
233
}
5✔
234

235
unsigned int RMC::getUtcFix() const noexcept {
8✔
236
    return utcFix_;
8✔
237
}
238

239
char RMC::getStatus() const noexcept {
8✔
240
    return status_;
8✔
241
}
242

243
double RMC::getLatitude() const noexcept {
9✔
244
    return latitude_;
9✔
245
}
246

247
char RMC::getLatitudeDirection() const noexcept {
8✔
248
    return latitudeDirection_;
8✔
249
}
250

251
double RMC::getLongitude() const noexcept {
9✔
252
    return longitude_;
9✔
253
}
254

255
char RMC::getLongitudeDirection() const noexcept {
8✔
256
    return longitudeDirection_;
8✔
257
}
258

259
double RMC::getSpeedOverGround() const noexcept {
8✔
260
    return speedOverGround_;
8✔
261
}
262

263
double RMC::getCourseOverGround() const noexcept {
8✔
264
    return courseOverGround_;
8✔
265
}
266

267
unsigned int RMC::getDate() const noexcept {
8✔
268
    return date_;
8✔
269
}
270

271
double RMC::getMagneticVariation() const noexcept {
8✔
272
    return magneticVariation_;
8✔
273
}
274

275
char RMC::getMagneticVariationDirection() const noexcept {
8✔
276
    return magneticVariationDirection_;
8✔
277
}
278

279
char RMC::getModeIndicator() const noexcept {
8✔
280
    return modeIndicator_;
8✔
281
}
282

283
char RMC::getNavigationStatus() const noexcept {
8✔
284
    return navigationStatus_;
8✔
285
}
286

287
bool RMC::operator==(const RMC& other) const noexcept {
1✔
288
    return Message0183::operator==(other);
1✔
289
}
290

291
} // namespace nmea0183
292
} // 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