nmealib 0.0.4
NMEA 0183/NMEA 2000 parsing library
Loading...
Searching...
No Matches
vtg.cpp
Go to the documentation of this file.
2
5
6#include <iomanip>
7#include <sstream>
8#include <vector>
9
10namespace nmealib {
11namespace nmea0183 {
12
13std::unique_ptr<VTG> VTG::create(std::unique_ptr<Message0183> baseMessage) {
14 std::string context = "VTG::create";
15 if (baseMessage->getSentenceType() != "VTG") {
16 NMEALIB_RETURN_ERROR(NotVTGException(context, "Expected sentence type 'VTG', got " + baseMessage->getSentenceType()));
17 }
18
19 std::string payload = baseMessage->getPayload();
20 std::istringstream ss(payload);
21 std::string token;
22 std::vector<std::string> fields;
23
24 while (std::getline(ss, token, ',')) {
25 fields.push_back(token);
26 }
27
28 if (!payload.empty() && payload.back() == ',') {
29 fields.push_back("");
30 }
31
32 if (!fields.empty()) {
33 fields.erase(fields.begin());
34 }
35
36 if (fields.size() != 4 && fields.size() != 8 && fields.size() != 9) {
37 NMEALIB_RETURN_ERROR(NotVTGException(context, "Invalid fields in VTG payload: expected 4, 8 or 9, got " + std::to_string(fields.size()) + ". Payload: " + payload));
38 }
39
40 bool legacyFormat = fields.size() == 4;
41 if (!legacyFormat && !fields[1].empty() && fields[1] != "T") {
42 legacyFormat = true;
43 }
44
45 if (legacyFormat) {
46 if (fields.size() != 4) {
47 NMEALIB_RETURN_ERROR(NotVTGException(context, "Legacy VTG format requires exactly 4 fields"));
48 }
49
50 double courseOverGroundTrue = 0.0;
51 double courseOverGroundMagnetic = 0.0;
52 double speedOverGroundKnots = 0.0;
53 double speedOverGroundKph = 0.0;
54 if (!detail::parseOptionalDouble(fields[0], courseOverGroundTrue) ||
55 !detail::parseOptionalDouble(fields[1], courseOverGroundMagnetic) ||
56 !detail::parseOptionalDouble(fields[2], speedOverGroundKnots) ||
57 !detail::parseOptionalDouble(fields[3], speedOverGroundKph)) {
58 NMEALIB_RETURN_ERROR(NmeaException(context, "Error parsing VTG fields"));
59 }
60
61 return std::unique_ptr<VTG>(new VTG(std::move(*baseMessage),
62 courseOverGroundTrue,
63 '\0',
64 courseOverGroundMagnetic,
65 '\0',
66 speedOverGroundKnots,
67 '\0',
68 speedOverGroundKph,
69 '\0',
70 std::nullopt,
71 true));
72 }
73
74 double courseOverGroundTrue = 0.0;
75 double courseOverGroundMagnetic = 0.0;
76 double speedOverGroundKnots = 0.0;
77 double speedOverGroundKph = 0.0;
78 if (!detail::parseOptionalDouble(fields[0], courseOverGroundTrue) ||
79 !detail::parseOptionalDouble(fields[2], courseOverGroundMagnetic) ||
80 !detail::parseOptionalDouble(fields[4], speedOverGroundKnots) ||
81 !detail::parseOptionalDouble(fields[6], speedOverGroundKph)) {
82 NMEALIB_RETURN_ERROR(NmeaException(context, "Error parsing VTG fields"));
83 }
84
85 char courseOverGroundTrueType = fields[1].empty() ? '\0' : fields[1][0];
86 char courseOverGroundMagneticType = fields[3].empty() ? '\0' : fields[3][0];
87 char speedOverGroundKnotsType = fields[5].empty() ? '\0' : fields[5][0];
88 char speedOverGroundKphType = fields[7].empty() ? '\0' : fields[7][0];
89 std::optional<char> faaModeIndicator = std::nullopt;
90
91 if (fields.size() == 9 && !fields[8].empty()) {
92 faaModeIndicator = fields[8][0];
93 }
94
95 return std::unique_ptr<VTG>(new VTG(std::move(*baseMessage),
96 courseOverGroundTrue,
97 courseOverGroundTrueType,
98 courseOverGroundMagnetic,
99 courseOverGroundMagneticType,
100 speedOverGroundKnots,
101 speedOverGroundKnotsType,
102 speedOverGroundKph,
103 speedOverGroundKphType,
104 faaModeIndicator,
105 false));
106}
107
108VTG::VTG(Message0183 baseMessage,
109 double courseOverGroundTrue,
110 char courseOverGroundTrueType,
111 double courseOverGroundMagnetic,
112 char courseOverGroundMagneticType,
113 double speedOverGroundKnots,
114 char speedOverGroundKnotsType,
115 double speedOverGroundKph,
116 char speedOverGroundKphType,
117 std::optional<char> faaModeIndicator,
118 bool legacyFormat) noexcept
119 : Message0183(std::move(baseMessage)),
120 courseOverGroundTrue_(courseOverGroundTrue),
121 courseOverGroundTrueType_(courseOverGroundTrueType),
122 courseOverGroundMagnetic_(courseOverGroundMagnetic),
123 courseOverGroundMagneticType_(courseOverGroundMagneticType),
124 speedOverGroundKnots_(speedOverGroundKnots),
125 speedOverGroundKnotsType_(speedOverGroundKnotsType),
126 speedOverGroundKph_(speedOverGroundKph),
127 speedOverGroundKphType_(speedOverGroundKphType),
128 faaModeIndicator_(faaModeIndicator),
129 legacyFormat_(legacyFormat) {}
130
131VTG::VTG(std::string talkerId,
132 double courseOverGroundTrue,
133 double courseOverGroundMagnetic,
134 double speedOverGroundKnots,
135 double speedOverGroundKph,
136 std::optional<char> faaModeIndicator,
137 bool legacyFormat)
138 : Message0183(*Message0183::create(composeRaw(talkerId,
139 courseOverGroundTrue,
140 courseOverGroundMagnetic,
141 speedOverGroundKnots,
142 speedOverGroundKph,
143 faaModeIndicator,
144 legacyFormat))),
145 courseOverGroundTrue_(courseOverGroundTrue),
146 courseOverGroundTrueType_(legacyFormat ? '\0' : 'T'),
147 courseOverGroundMagnetic_(courseOverGroundMagnetic),
148 courseOverGroundMagneticType_(legacyFormat ? '\0' : 'M'),
149 speedOverGroundKnots_(speedOverGroundKnots),
150 speedOverGroundKnotsType_(legacyFormat ? '\0' : 'N'),
151 speedOverGroundKph_(speedOverGroundKph),
152 speedOverGroundKphType_(legacyFormat ? '\0' : 'K'),
153 faaModeIndicator_(legacyFormat ? std::nullopt : faaModeIndicator),
154 legacyFormat_(legacyFormat) {}
155
156std::unique_ptr<nmealib::Message> VTG::clone() const {
157 return std::unique_ptr<VTG>(new VTG(*this));
158}
159
160std::string VTG::getStringContent(bool verbose) const noexcept {
161 std::ostringstream ss;
162 ss << this->toString(verbose);
163 ss << std::fixed << std::setprecision(2);
164
165 if (verbose) {
166 ss << "\tLegacy Format: " << (legacyFormat_ ? "Yes" : "No") << "\n";
167 ss << "\tCourse Over Ground: " << courseOverGroundTrue_ << "T\n";
168 ss << "\tCourse Over Ground: " << courseOverGroundMagnetic_ << "M\n";
169 ss << "\tSpeed Over Ground: " << speedOverGroundKnots_ << "kts\n";
170 ss << "\tSpeed Over Ground: " << speedOverGroundKph_ << "kph";
171 if (faaModeIndicator_.has_value()) {
172 ss << "\n"
173 << "\tFAA Mode Indicator: " << faaModeIndicator_.value();
174 }
175 ss << "\n";
176 } else {
177 ss << "True=" << courseOverGroundTrue_
178 << ", Magnetic=" << courseOverGroundMagnetic_
179 << ", Knots=" << speedOverGroundKnots_
180 << ", KPH=" << speedOverGroundKph_;
181 if (faaModeIndicator_.has_value()) {
182 ss << ", FAA=" << faaModeIndicator_.value();
183 }
184 ss << ", Legacy=" << (legacyFormat_ ? "Y" : "N");
185 }
186
187 return ss.str();
188}
189
190std::string VTG::composeRaw(const std::string& talkerId,
191 double courseOverGroundTrue,
192 double courseOverGroundMagnetic,
193 double speedOverGroundKnots,
194 double speedOverGroundKph,
195 std::optional<char> faaModeIndicator,
196 bool legacyFormat) {
197 std::ostringstream payloadStream;
198 payloadStream << talkerId << "VTG,";
199
200 if (legacyFormat) {
201 payloadStream << std::fixed << std::setprecision(2) << courseOverGroundTrue << ",";
202 payloadStream << std::fixed << std::setprecision(2) << courseOverGroundMagnetic << ",";
203 payloadStream << std::fixed << std::setprecision(3) << speedOverGroundKnots << ",";
204 payloadStream << std::fixed << std::setprecision(3) << speedOverGroundKph;
205 } else {
206 payloadStream << std::fixed << std::setprecision(2) << courseOverGroundTrue << ",T,";
207 payloadStream << std::fixed << std::setprecision(2) << courseOverGroundMagnetic << ",M,";
208 payloadStream << std::fixed << std::setprecision(3) << speedOverGroundKnots << ",N,";
209 payloadStream << std::fixed << std::setprecision(3) << speedOverGroundKph << ",K";
210
211 if (faaModeIndicator.has_value()) {
212 payloadStream << "," << faaModeIndicator.value();
213 }
214 }
215
216 std::string payload = payloadStream.str();
217 return "$" + payload + "\r\n";
218}
219
220double VTG::getCourseOverGroundTrue() const noexcept {
221 return courseOverGroundTrue_;
222}
223
224char VTG::getCourseOverGroundTrueType() const noexcept {
225 return courseOverGroundTrueType_;
226}
227
228double VTG::getCourseOverGroundMagnetic() const noexcept {
229 return courseOverGroundMagnetic_;
230}
231
233 return courseOverGroundMagneticType_;
234}
235
236double VTG::getSpeedOverGroundKnots() const noexcept {
237 return speedOverGroundKnots_;
238}
239
240char VTG::getSpeedOverGroundKnotsType() const noexcept {
241 return speedOverGroundKnotsType_;
242}
243
244double VTG::getSpeedOverGroundKph() const noexcept {
245 return speedOverGroundKph_;
246}
247
248char VTG::getSpeedOverGroundKphType() const noexcept {
249 return speedOverGroundKphType_;
250}
251
252bool VTG::hasFaaModeIndicator() const noexcept {
253 return faaModeIndicator_.has_value();
254}
255
256std::optional<char> VTG::getFaaModeIndicator() const noexcept {
257 return faaModeIndicator_;
258}
259
260bool VTG::isLegacyFormat() const noexcept {
261 return legacyFormat_;
262}
263
264bool VTG::operator==(const VTG& other) const noexcept {
265 return Message0183::operator==(other);
266}
267
268} // namespace nmea0183
269} // 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 VTG (Course Over Ground and Ground Speed) sentence.
Definition vtg.h:44
char getSpeedOverGroundKphType() const noexcept
Get km/h unit indicator (typically 'K', or '\0' in legacy format).
Definition vtg.cpp:248
double getSpeedOverGroundKnots() const noexcept
Get speed over ground in knots.
Definition vtg.cpp:236
bool isLegacyFormat() const noexcept
Return whether this sentence uses legacy compact VTG format.
Definition vtg.cpp:260
double getCourseOverGroundMagnetic() const noexcept
Get magnetic course over ground in degrees.
Definition vtg.cpp:228
VTG(std::string talkerId, double courseOverGroundTrue, double courseOverGroundMagnetic, double speedOverGroundKnots, double speedOverGroundKph, std::optional< char > faaModeIndicator=std::nullopt, bool legacyFormat=false)
Construct a VTG message from individual field values.
Definition vtg.cpp:131
bool operator==(const VTG &other) const noexcept
Compare two VTG messages for equality.
Definition vtg.cpp:264
char getCourseOverGroundTrueType() const noexcept
Get true course type indicator (typically 'T', or '\0' in legacy format).
Definition vtg.cpp:224
std::unique_ptr< nmealib::Message > clone() const override
Create a polymorphic copy of this VTG message.
Definition vtg.cpp:156
char getSpeedOverGroundKnotsType() const noexcept
Get knots unit indicator (typically 'N', or '\0' in legacy format).
Definition vtg.cpp:240
double getCourseOverGroundTrue() const noexcept
Get true course over ground in degrees.
Definition vtg.cpp:220
std::string getStringContent(bool verbose) const noexcept override
Return a human-readable string representation of this message.
Definition vtg.cpp:160
char getCourseOverGroundMagneticType() const noexcept
Get magnetic course type indicator (typically 'M', or '\0' in legacy format).
Definition vtg.cpp:232
bool hasFaaModeIndicator() const noexcept
Return whether FAA mode indicator is present.
Definition vtg.cpp:252
double getSpeedOverGroundKph() const noexcept
Get speed over ground in kilometers per hour.
Definition vtg.cpp:244
std::optional< char > getFaaModeIndicator() const noexcept
Get optional FAA mode indicator.
Definition vtg.cpp:256
#define NMEALIB_RETURN_ERROR(exceptionExpr)
bool parseOptionalDouble(const std::string &text, double &value) noexcept
Definition parse.h:86