nmealib 0.0.4
NMEA 0183/NMEA 2000 parsing library
Loading...
Searching...
No Matches
rmc.cpp
Go to the documentation of this file.
2
5#include <cmath>
6#include <iomanip>
7#include <limits>
8#include <sstream>
9#include <vector>
10
12
13namespace nmealib {
14namespace nmea0183 {
15
16std::unique_ptr<RMC> RMC::create(std::unique_ptr<Message0183> baseMessage) {
17 std::string context = "RMC::create";
18 if (baseMessage->getSentenceType() != "RMC") {
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();
24 std::istringstream ss(payload);
25 std::string token;
26 std::vector<std::string> fields;
27
28 while (std::getline(ss, token, ',')) {
29 fields.push_back(token);
30 }
31
32 // Drop first element which is the sentence type (e.g. "GPRMC")
33 if (!fields.empty()) {
34 fields.erase(fields.begin());
35 }
36
37 size_t messageSize = fields.size();
38 if (messageSize != 11 && messageSize != 12 && messageSize != 13) {
39 NMEALIB_RETURN_ERROR(NotRMCException(context, "Insufficient fields in RMC payload: expected 11, 12 or 13, got " + std::to_string(fields.size()) + ". Payload: " + payload));
40 }
41
42 unsigned int utcFix = 0U;
43 double latitude = 0.0;
44 double longitude = 0.0;
45 double speedOverGround = 0.0;
46 double courseOverGround = 0.0;
47 unsigned int date = 0U;
48 double magneticVariation = 0.0;
49
50 auto parseUtcFix = [](const std::string& utcField, unsigned int& outUtcFix) {
51 if (utcField.empty()) {
52 outUtcFix = 0U;
53 return true;
54 }
55
56 double utcDouble = 0.0;
57 if (!detail::parseOptionalDouble(utcField, utcDouble) || utcDouble < 0.0 ||
58 utcDouble > static_cast<double>(std::numeric_limits<unsigned int>::max())) {
59 return false;
60 }
61
62 outUtcFix = static_cast<unsigned int>(utcDouble);
63 return true;
64 };
65
66 if (!parseUtcFix(fields[0], utcFix) ||
67 !detail::parseNmeaCoordinate(fields[2], latitude) ||
68 !detail::parseNmeaCoordinate(fields[4], longitude) ||
69 !detail::parseOptionalDouble(fields[6], speedOverGround) ||
70 !detail::parseOptionalDouble(fields[7], courseOverGround) ||
71 !detail::parseOptionalUnsigned(fields[8], date) ||
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];
77 char latDirection = fields[3].empty() ? '\0' : fields[3][0];
78 char lonDirection = fields[5].empty() ? '\0' : fields[5][0];
79 char magneticVariationDirection = fields[10].empty() ? '\0' : fields[10][0];
80 char modeIndicator = '\0';
81 char navigationStatus = '\0';
82
83 if (messageSize >= 12) {
84 modeIndicator = fields[11].empty() ? '\0' : fields[11][0];
85 }
86
87 if (messageSize == 13) {
88 navigationStatus = fields[12].empty() ? '\0' : fields[12][0];
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));
92}
93
94RMC::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
108 : Message0183(std::move(baseMessage)),
109 utcFix_(utcFix),
110 status_(status),
111 latitude_(latitude),
112 latitudeDirection_(latitudeDirection),
113 longitude_(longitude),
114 longitudeDirection_(longitudeDirection),
115 speedOverGround_(speedOverGround),
116 courseOverGround_(courseOverGround),
117 date_(date),
118 magneticVariation_(magneticVariation),
119 magneticVariationDirection_(magneticVariationDirection),
120 modeIndicator_(modeIndicator),
121 navigationStatus_(navigationStatus) {}
122
123RMC::RMC(std::string talkerId, unsigned int utcFix,
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) :
136 Message0183(*Message0183::create(composeRaw(talkerId, utcFix, status, latitude,
137 latitudeDirection, longitude, longitudeDirection,
138 speedOverGround, courseOverGround, date, magneticVariation,
139 magneticVariationDirection, modeIndicator, navigationStatus))),
140 utcFix_(utcFix),
141 status_(status),
142 latitude_(latitude),
143 latitudeDirection_(latitudeDirection),
144 longitude_(longitude),
145 longitudeDirection_(longitudeDirection),
146 speedOverGround_(speedOverGround),
147 courseOverGround_(courseOverGround),
148 date_(date),
149 magneticVariation_(magneticVariation),
150 magneticVariationDirection_(magneticVariationDirection),
151 modeIndicator_(modeIndicator),
152 navigationStatus_(navigationStatus) {}
153
154std::unique_ptr<nmealib::Message> RMC::clone() const {
155 return std::unique_ptr<RMC>(new RMC(*this));
156}
157
158std::string RMC::getStringContent(bool verbose) const noexcept {
159 std::ostringstream ss;
160 ss << this->toString(verbose);
161 std::ostringstream latStream;
162 latStream << std::setprecision(10) << latitude_;
163 const std::string latitudeStr = latStream.str();
164
165 std::ostringstream lonStream;
166 lonStream << std::setprecision(10) << longitude_;
167 const std::string longitudeStr = lonStream.str();
168
169 if (verbose) {
170 ss << "\tUTC Fix: " << utcFix_ << "\n";
171 ss << "\tStatus: " << status_ << "\n";
172 ss << "\tLatitude: " << latitudeStr << " " << latitudeDirection_ << "\n";
173 ss << "\tLongitude: " << longitudeStr << " " << longitudeDirection_ << "\n";
174 ss << "\tSpeed Over Ground: " << speedOverGround_ << "\n";
175 ss << "\tCourse Over Ground: " << courseOverGround_ << "\n";
176 ss << "\tDate: " << date_ << "\n";
177 ss << "\tMagnetic Variation: " << magneticVariation_ << " " << magneticVariationDirection_ << "\n";
178 ss << "\tMode Indicator: " << modeIndicator_ << "\n";
179 ss << "\tNavigation Status: " << navigationStatus_;
180 ss << "\n";
181 } else {
182 ss << "UTC Fix=" << utcFix_
183 << ", Status=" << status_
184 << ", Lat=" << latitudeStr << latitudeDirection_
185 << ", Lon=" << longitudeStr << longitudeDirection_
186 << ", SOG=" << speedOverGround_
187 << ", COG=" << courseOverGround_
188 << ", Date=" << date_
189 << ", MagVar=" << magneticVariation_ << magneticVariationDirection_
190 << ", Mode=" << modeIndicator_
191 << ", NavStatus=" << navigationStatus_;
192 }
193 return ss.str();
194}
195
196std::string RMC::composeRaw(const std::string& talkerId, unsigned int utcFix,
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;
210 payloadStream << talkerId << "RMC,";
211 payloadStream << std::setw(6) << std::setfill('0') << utcFix << ",";
212 payloadStream << status << ",";
213 double latitudeDegrees = std::floor(latitude);
214 double latitudeMinutes = (latitude - latitudeDegrees) * 60.0;
215 latitude = latitudeDegrees * 100.0 + latitudeMinutes; // Convert from decimal degrees to ddmm.mmmm
216 double longitudeDegrees = std::floor(longitude);
217 double longitudeMinutes = (longitude - longitudeDegrees) * 60.0;
218 longitude = longitudeDegrees * 100.0 + longitudeMinutes; // Convert from decimal degrees to dddmm.mmmm
219 payloadStream << std::fixed << std::setprecision(4) << latitude << ",";
220 payloadStream << latitudeDirection << ",";
221 payloadStream << std::fixed << std::setprecision(4) << longitude << ",";
222 payloadStream << longitudeDirection << ",";
223 payloadStream << std::fixed << std::setprecision(1) << speedOverGround << ",";
224 payloadStream << std::fixed << std::setprecision(1) << courseOverGround << ",";
225 payloadStream << std::setw(6) << std::setfill('0') << date << ",";
226 payloadStream << std::fixed << std::setprecision(1) << magneticVariation << ",";
227 payloadStream << magneticVariationDirection << ",";
228 payloadStream << modeIndicator << ",";
229 payloadStream << navigationStatus;
230
231 std::string payload = payloadStream.str();
232 return "$" + payload + "\r\n";
233}
234
235unsigned int RMC::getUtcFix() const noexcept {
236 return utcFix_;
237}
238
239char RMC::getStatus() const noexcept {
240 return status_;
241}
242
243double RMC::getLatitude() const noexcept {
244 return latitude_;
245}
246
247char RMC::getLatitudeDirection() const noexcept {
248 return latitudeDirection_;
249}
250
251double RMC::getLongitude() const noexcept {
252 return longitude_;
253}
254
255char RMC::getLongitudeDirection() const noexcept {
256 return longitudeDirection_;
257}
258
259double RMC::getSpeedOverGround() const noexcept {
260 return speedOverGround_;
261}
262
263double RMC::getCourseOverGround() const noexcept {
264 return courseOverGround_;
265}
266
267unsigned int RMC::getDate() const noexcept {
268 return date_;
269}
270
271double RMC::getMagneticVariation() const noexcept {
272 return magneticVariation_;
273}
274
276 return magneticVariationDirection_;
277}
278
279char RMC::getModeIndicator() const noexcept {
280 return modeIndicator_;
281}
282
283char RMC::getNavigationStatus() const noexcept {
284 return navigationStatus_;
285}
286
287bool RMC::operator==(const RMC& other) const noexcept {
288 return Message0183::operator==(other);
289}
290
291} // namespace nmea0183
292} // namespace nmealib
Represents an NMEA 0183 sentence.
Definition nmea0183.h:98
bool operator==(const Message0183 &other) const noexcept
Compares two Message0183 objects for equality based on their content and timestamp.
Definition nmea0183.cpp:204
Represents a parsed NMEA 0183 RMC (Recommended Minimum Navigation Information) sentence.
Definition rmc.h:37
double getCourseOverGround() const noexcept
Get course over ground in degrees.
Definition rmc.cpp:263
char getLatitudeDirection() const noexcept
Get latitude hemisphere indicator ('N' or 'S').
Definition rmc.cpp:247
char getMagneticVariationDirection() const noexcept
Get magnetic variation direction ('E' or 'W').
Definition rmc.cpp:275
double getMagneticVariation() const noexcept
Get magnetic variation in degrees.
Definition rmc.cpp:271
double getSpeedOverGround() const noexcept
Get speed over ground in knots.
Definition rmc.cpp:259
char getStatus() const noexcept
Get status indicator.
Definition rmc.cpp:239
std::string getStringContent(bool verbose) const noexcept override
Return a human-readable string representation of this message.
Definition rmc.cpp:158
double getLatitude() const noexcept
Get latitude in decimal degrees.
Definition rmc.cpp:243
char getModeIndicator() const noexcept
Get mode indicator character.
Definition rmc.cpp:279
std::unique_ptr< nmealib::Message > clone() const override
Create a polymorphic copy of this RMC message.
Definition rmc.cpp:154
unsigned int getUtcFix() const noexcept
Get UTC fix time in hhmmss[.ss] numeric form.
Definition rmc.cpp:235
RMC(std::string talkerId, unsigned int utcFix, char status, double latitude, char latitudeDirection, double longitude, char longitudeDirection, double speedOverGround, double courseOverGround, unsigned int date, double magneticVariation, char magneticVariationDirection, char modeIndicator, char navigationStatus)
Construct an RMC message from individual field values.
Definition rmc.cpp:123
double getLongitude() const noexcept
Get longitude in decimal degrees.
Definition rmc.cpp:251
bool operator==(const RMC &other) const noexcept
Compare two RMC messages for equality.
Definition rmc.cpp:287
unsigned int getDate() const noexcept
Get date in ddmmyy numeric form.
Definition rmc.cpp:267
char getNavigationStatus() const noexcept
Get navigation status character.
Definition rmc.cpp:283
char getLongitudeDirection() const noexcept
Get longitude hemisphere indicator ('E' or 'W').
Definition rmc.cpp:255
#define NMEALIB_RETURN_ERROR(exceptionExpr)
bool parseNmeaCoordinate(const std::string &text, double &value) noexcept
Definition parse.h:118
bool parseOptionalUnsigned(const std::string &text, unsigned int &value) noexcept
Definition parse.h:102
bool parseOptionalDouble(const std::string &text, double &value) noexcept
Definition parse.h:86