nmealib 0.0.4
NMEA 0183/NMEA 2000 parsing library
Loading...
Searching...
No Matches
gga.cpp
Go to the documentation of this file.
2
5
6#include <cmath>
7#include <iomanip>
8#include <sstream>
9#include <vector>
10
11namespace nmealib {
12namespace nmea0183 {
13
14std::unique_ptr<GGA> GGA::create(std::unique_ptr<Message0183> baseMessage) {
15 std::string context = "GGA::create";
16 if (baseMessage->getSentenceType() != "GGA") {
17 NMEALIB_RETURN_ERROR(NotGGAException(context, "Expected sentence type 'GGA', got " + baseMessage->getSentenceType()));
18 }
19
20 // Parse the payload to extract GGA-specific fields
21 std::string payload = baseMessage->getPayload();
22 std::istringstream ss(payload);
23 std::string token;
24 std::vector<std::string> fields;
25
26 while (std::getline(ss, token, ',')) {
27 fields.push_back(token);
28 }
29
30 // Preserve the final empty field when payload ends with a comma.
31 if (!payload.empty() && payload.back() == ',') {
32 fields.push_back("");
33 }
34
35 // Drop first element which is the sentence type (e.g. "GPGGA")
36 if (!fields.empty()) {
37 fields.erase(fields.begin());
38 }
39 size_t messageSize = fields.size();
40 if (messageSize != 14 && messageSize != 12) {
41 NMEALIB_RETURN_ERROR(NotGGAException(context, "Insufficient fields in GGA payload: expected 14 or 12, got " + std::to_string(fields.size()) + ". Payload: " + payload));
42 }
43
44 double timestamp = 0.0;
45 double latitude = 0.0;
46 double longitude = 0.0;
47 unsigned int gpsQuality = 0U;
48 unsigned int satellites = 0U;
49 double hdop = 0.0;
50 double altitude = 0.0;
51 double geoidalSeparation = 0.0;
52 double dgpsAge = -1.0;
53 if (!detail::parseOptionalDouble(fields[0], timestamp) ||
54 !detail::parseNmeaCoordinate(fields[1], latitude) ||
55 !detail::parseNmeaCoordinate(fields[3], longitude) ||
56 !detail::parseOptionalUnsigned(fields[5], gpsQuality) ||
57 !detail::parseOptionalUnsigned(fields[6], satellites) ||
58 !detail::parseOptionalDouble(fields[7], hdop) ||
59 !detail::parseOptionalDouble(fields[8], altitude) ||
60 !detail::parseOptionalDouble(fields[10], geoidalSeparation)) {
61 NMEALIB_RETURN_ERROR(NmeaException(context, "Error parsing GGA fields"));
62 }
63
64 char latitudeDirection = fields[2].empty() ? '\0' : fields[2][0];
65 char longitudeDirection = fields[4].empty() ? '\0' : fields[4][0];
66 char altitudeUnits = fields[9].empty() ? '\0' : fields[9][0];
67 char geoidalSeparationUnits = fields[11].empty() ? '\0' : fields[11][0];
68 std::string dgpsReferenceStationId;
69
70 if (messageSize == 14) {
71 if (!detail::parseOptionalDouble(fields[12], dgpsAge)) {
72 NMEALIB_RETURN_ERROR(NmeaException(context, "Error parsing GGA fields"));
73 }
74 dgpsReferenceStationId = fields[13].empty() ? "" : fields[13];
75 }
76
77 return std::unique_ptr<GGA>(new GGA(std::move(*baseMessage),
78 timestamp,
79 latitude,
80 latitudeDirection,
81 longitude,
82 longitudeDirection,
83 gpsQuality,
84 satellites,
85 hdop,
86 altitude,
87 altitudeUnits,
88 geoidalSeparation,
89 geoidalSeparationUnits,
90 dgpsAge,
91 dgpsReferenceStationId));
92}
93
94GGA::GGA(Message0183 baseMessage,
95 double timestamp,
96 double latitude,
97 char latitudeDirection,
98 double longitude,
99 char longitudeDirection,
100 unsigned int gpsQuality,
101 unsigned int satellites,
102 double hdop,
103 double altitude,
104 char altitudeUnits,
105 double geoidalSeparation,
106 char geoidalSeparationUnits,
107 double dgpsAge,
108 std::string dgpsReferenceStationId) noexcept
109 : Message0183(std::move(baseMessage)),
110 utcTime_(timestamp),
111 latitude_(latitude),
112 latitudeDirection_(latitudeDirection),
113 longitude_(longitude),
114 longitudeDirection_(longitudeDirection),
115 gpsQuality_(gpsQuality),
116 satellites_(satellites),
117 hdop_(hdop),
118 altitude_(altitude),
119 altitudeUnits_(altitudeUnits),
120 geoidalSeparation_(geoidalSeparation),
121 geoidalSeparationUnits_(geoidalSeparationUnits),
122 dgpsAge_(dgpsAge),
123 dgpsReferenceStationId_(std::move(dgpsReferenceStationId)) {}
124
125GGA::GGA(std::string talkerId,
126 double timestamp,
127 double latitude,
128 char latitudeDirection,
129 double longitude,
130 char longitudeDirection,
131 unsigned int gpsQuality,
132 unsigned int satellites,
133 double hdop,
134 double altitude,
135 char altitudeUnits,
136 double geoidalSeparation,
137 char geoidalSeparationUnits,
138 double dgpsAge,
139 std::string dgpsReferenceStationId)
140 : Message0183(*Message0183::create(composeRaw(talkerId,
141 timestamp,
142 latitude,
143 latitudeDirection,
144 longitude,
145 longitudeDirection,
146 gpsQuality,
147 satellites,
148 hdop,
149 altitude,
150 altitudeUnits,
151 geoidalSeparation,
152 geoidalSeparationUnits,
153 dgpsAge,
154 dgpsReferenceStationId))),
155 utcTime_(timestamp),
156 latitude_(latitude),
157 latitudeDirection_(latitudeDirection),
158 longitude_(longitude),
159 longitudeDirection_(longitudeDirection),
160 gpsQuality_(gpsQuality),
161 satellites_(satellites),
162 hdop_(hdop),
163 altitude_(altitude),
164 altitudeUnits_(altitudeUnits),
165 geoidalSeparation_(geoidalSeparation),
166 geoidalSeparationUnits_(geoidalSeparationUnits),
167 dgpsAge_(dgpsAge),
168 dgpsReferenceStationId_(std::move(dgpsReferenceStationId)) {}
169
170std::unique_ptr<nmealib::Message> GGA::clone() const {
171 return std::unique_ptr<GGA>(new GGA(*this));
172}
173
174std::string GGA::getStringContent(bool verbose) const noexcept {
175 std::ostringstream ss;
176 ss << this->toString(verbose);
177 std::ostringstream latStream;
178 latStream << std::setprecision(10) << latitude_;
179 const std::string latitudeStr = latStream.str();
180
181 std::ostringstream lonStream;
182 lonStream << std::setprecision(10) << longitude_;
183 const std::string longitudeStr = lonStream.str();
184
185 if (verbose) {
186 ss << "\tTimestamp: " << utcTime_ << "\n";
187 ss << "\tLatitude: " << latitudeStr << " " << latitudeDirection_ << "\n";
188 ss << "\tLongitude: " << longitudeStr << " " << longitudeDirection_ << "\n";
189 ss << "\tGPS Quality: " << gpsQuality_ << "\n";
190 ss << "\tSatellites: " << satellites_ << "\n";
191 ss << "\tHDOP: " << hdop_ << "\n";
192 ss << "\tAltitude: " << altitude_ << " " << altitudeUnits_ << "\n";
193 ss << "\tGeoidal Separation: " << geoidalSeparation_ << " " << geoidalSeparationUnits_ << "\n";
194 ss << "\tDGPS Age: " << dgpsAge_ << "\n";
195 ss << "\tDGPS Ref: " << dgpsReferenceStationId_;
196 ss << "\n";
197 } else {
198 ss << "Time=" << utcTime_
199 << ", Lat=" << latitudeStr << latitudeDirection_
200 << ", Lon=" << longitudeStr << longitudeDirection_
201 << ", Qual=" << gpsQuality_
202 << ", Sats=" << satellites_
203 << ", HDOP=" << hdop_
204 << ", Alt=" << altitude_ << altitudeUnits_
205 << ", GeoSep=" << geoidalSeparation_ << geoidalSeparationUnits_
206 << ", DGPSAge=" << dgpsAge_
207 << ", DGPSRef=" << dgpsReferenceStationId_;
208 }
209 return ss.str();
210}
211
212std::string GGA::composeRaw(const std::string& talkerId,
213 double timestamp,
214 double latitude,
215 char latitudeDirection,
216 double longitude,
217 char longitudeDirection,
218 unsigned int gpsQuality,
219 unsigned int satellites,
220 double hdop,
221 double altitude,
222 char altitudeUnits,
223 double geoidalSeparation,
224 char geoidalSeparationUnits,
225 double dgpsAge,
226 const std::string& dgpsReferenceStationId) {
227 std::ostringstream payloadStream;
228 payloadStream << talkerId << "GGA,";
229 payloadStream << std::fixed << std::setprecision(2) << std::setw(9) << std::setfill('0') << timestamp << ",";
230
231 double latitudeDegrees = std::floor(latitude);
232 double latitudeMinutes = (latitude - latitudeDegrees) * 60.0;
233 double latitudeNmea = latitudeDegrees * 100.0 + latitudeMinutes; // ddmm.mmmm
234
235 double longitudeDegrees = std::floor(longitude);
236 double longitudeMinutes = (longitude - longitudeDegrees) * 60.0;
237 double longitudeNmea = longitudeDegrees * 100.0 + longitudeMinutes; // dddmm.mmmm
238
239 payloadStream << std::fixed << std::setprecision(6) << std::setw(10) << std::setfill('0') << latitudeNmea << ",";
240 payloadStream << latitudeDirection << ",";
241 payloadStream << std::fixed << std::setprecision(6) << std::setw(11) << std::setfill('0') << longitudeNmea << ",";
242 payloadStream << longitudeDirection << ",";
243 payloadStream << gpsQuality << ",";
244 payloadStream << std::setw(2) << std::setfill('0') << satellites << ",";
245 payloadStream << std::fixed << std::setprecision(1) << hdop << ",";
246 payloadStream << std::fixed << std::setprecision(1) << altitude << ",";
247 payloadStream << altitudeUnits << ",";
248
249 if (geoidalSeparation == 0.0) {
250 payloadStream << ",";
251 } else {
252 payloadStream << std::fixed << std::setprecision(1) << geoidalSeparation << ",";
253 }
254
255 payloadStream << geoidalSeparationUnits << ",";
256
257 if (dgpsAge == 0.0) {
258 payloadStream << ",";
259 } else {
260 payloadStream << std::fixed << std::setprecision(1) << dgpsAge << ",";
261 }
262
263 payloadStream << dgpsReferenceStationId;
264
265 std::string payload = payloadStream.str();
266 return "$" + payload + "\r\n";
267}
268
269double GGA::getUtcTime() const noexcept {
270 return utcTime_;
271}
272
273double GGA::getLatitude() const noexcept {
274 return latitude_;
275}
276
277char GGA::getLatitudeDirection() const noexcept {
278 return latitudeDirection_;
279}
280
281double GGA::getLongitude() const noexcept {
282 return longitude_;
283}
284
285char GGA::getLongitudeDirection() const noexcept {
286 return longitudeDirection_;
287}
288
289unsigned int GGA::getGpsQuality() const noexcept {
290 return gpsQuality_;
291}
292
293unsigned int GGA::getSatellites() const noexcept {
294 return satellites_;
295}
296
297double GGA::getHdop() const noexcept {
298 return hdop_;
299}
300
301double GGA::getAltitude() const noexcept {
302 return altitude_;
303}
304
305char GGA::getAltitudeUnits() const noexcept {
306 return altitudeUnits_;
307}
308
309double GGA::getGeoidalSeparation() const noexcept {
310 return geoidalSeparation_;
311}
312
313char GGA::getGeoidalSeparationUnits() const noexcept {
314 return geoidalSeparationUnits_;
315}
316
317double GGA::getDgpsAge() const noexcept {
318 return dgpsAge_;
319}
320
321std::string GGA::getDgpsReferenceStationId() const noexcept {
322 return dgpsReferenceStationId_;
323}
324
325bool GGA::operator==(const GGA& other) const noexcept {
326 return Message0183::operator==(other);
327}
328
329} // namespace nmea0183
330} // namespace nmealib
Represents a parsed NMEA 0183 GGA (Global Positioning System Fix Data) sentence.
Definition gga.h:36
unsigned int getGpsQuality() const noexcept
Get GPS quality indicator.
Definition gga.cpp:289
double getLatitude() const noexcept
Get latitude in decimal degrees.
Definition gga.cpp:273
unsigned int getSatellites() const noexcept
Get number of satellites used.
Definition gga.cpp:293
std::unique_ptr< nmealib::Message > clone() const override
Create a polymorphic copy of this GGA message.
Definition gga.cpp:170
double getDgpsAge() const noexcept
Get age of DGPS data in seconds.
Definition gga.cpp:317
char getLongitudeDirection() const noexcept
Get longitude hemisphere indicator ('E' or 'W').
Definition gga.cpp:285
std::string getStringContent(bool verbose) const noexcept override
Return a human-readable string representation of this message.
Definition gga.cpp:174
char getAltitudeUnits() const noexcept
Get altitude units (typically 'M').
Definition gga.cpp:305
char getGeoidalSeparationUnits() const noexcept
Get geoidal separation units (typically 'M').
Definition gga.cpp:313
GGA(std::string talkerId, double timestamp, double latitude, char latitudeDirection, double longitude, char longitudeDirection, unsigned int gpsQuality, unsigned int satellites, double hdop, double altitude, char altitudeUnits, double geoidalSeparation, char geoidalSeparationUnits, double dgpsAge, std::string dgpsReferenceStationId)
Construct a GGA message from individual field values.
Definition gga.cpp:125
std::string getDgpsReferenceStationId() const noexcept
Get DGPS reference station identifier.
Definition gga.cpp:321
double getLongitude() const noexcept
Get longitude in decimal degrees.
Definition gga.cpp:281
char getLatitudeDirection() const noexcept
Get latitude hemisphere indicator ('N' or 'S').
Definition gga.cpp:277
double getGeoidalSeparation() const noexcept
Get geoidal separation value.
Definition gga.cpp:309
double getUtcTime() const noexcept
Get UTC fix time in hhmmss.ss numeric form.
Definition gga.cpp:269
bool operator==(const GGA &other) const noexcept
Compare two GGA messages for equality.
Definition gga.cpp:325
double getHdop() const noexcept
Get horizontal dilution of precision.
Definition gga.cpp:297
double getAltitude() const noexcept
Get altitude value.
Definition gga.cpp:301
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
#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