Pico GPS Teseo
Toggle main menu visibility
Loading...
Searching...
No Matches
gps_nmea_lib-src
nmea
nmea.cpp
Go to the documentation of this file.
1
/*
2
* nmea.cpp
3
*
4
* Created on: 29 jul. 2024
5
* Author: jancu
6
*/
7
8
module
;
9
10
#include <cassert>
11
#include <ranges>
12
#include <cmath>
13
#include <chrono>
14
15
module
nmea
;
16
17
using
std::operator
""
sv;
18
constexpr
auto
delim
{
","
sv};
19
20
namespace
nmea
{
21
22
talker_id
nmea::talker
(
const
std::string_view& sv) {
23
talker_id
id
=
talker_id::gps
;
24
if
(sv.starts_with(
"$GP"
)) {
25
id
=
talker_id::gps
;
26
}
else
if
(sv.starts_with(
"$GL"
)) {
27
id
=
talker_id::glonass
;
28
}
else
if
(sv.starts_with(
"$GA"
)) {
29
id
=
talker_id::galileo
;
30
}
else
if
(sv.starts_with(
"$BD"
)) {
31
id
=
talker_id::beidou
;
32
}
else
if
(sv.starts_with(
"$QZ"
)) {
33
id
=
talker_id::qzss
;
34
}
else
if
(sv.starts_with(
"$GN"
)) {
35
id
=
talker_id::multiconstellation
;
36
}
37
return
id;
38
}
39
40
talker_id
nmea::system
(
const
std::string_view& sv) {
41
talker_id
id
=
talker_id::gps
;
42
if
(sv.starts_with(
"1"
)) {
43
id
=
talker_id::gps
;
44
}
else
if
(sv.starts_with(
"2"
)) {
45
id
=
talker_id::glonass
;
46
}
else
if
(sv.starts_with(
"3"
)) {
47
id
=
talker_id::galileo
;
48
}
else
if
(sv.starts_with(
"4"
)) {
49
id
=
talker_id::beidou
;
50
}
else
if
(sv.starts_with(
"5"
)) {
51
id
=
talker_id::qzss
;
52
}
53
return
id;
54
}
55
56
float
nmea::coord
(
const
unsigned
int
degrees_chars,
const
std::string_view& sv) {
57
std::string
s
(sv);
58
// a float (and a double) may not precisely represent the value in the message
59
float
coord
= std::stof(
s
.substr(0, degrees_chars),
nullptr
);
60
coord
+= std::stof(
s
.substr(degrees_chars),
nullptr
) / 60.0;
61
return
coord
;
62
}
63
64
direction
nmea::dir
(
const
std::string_view& sv) {
65
assert(sv.starts_with(
'N'
) ||
66
sv.starts_with(
'S'
) ||
67
sv.starts_with(
'E'
) ||
68
sv.starts_with(
'W'
));
69
direction
d =
direction::n
;
70
if
(sv.starts_with(
'N'
)) {
71
d =
direction::n
;
72
}
else
if
(sv.starts_with(
'S'
)) {
73
d =
direction::s
;
74
}
else
if
(sv.starts_with(
'E'
)) {
75
d =
direction::e
;
76
}
else
if
(sv.starts_with(
'W'
)) {
77
d =
direction::w
;
78
}
79
return
d;
80
}
81
82
time_t
nmea::time
(
const
std::string_view& sv) {
83
std::string
s
(sv);
84
return
time_t
{
85
std::chrono::hours(std::stoi(
s
.substr(0, 2))) +
86
std::chrono::minutes(std::stoi(
s
.substr(2, 2))) +
87
std::chrono::seconds(std::stoi(
s
.substr(4, 2))) +
88
std::chrono::milliseconds(std::stoi(
s
.substr(7)))
89
};
90
}
91
92
std::chrono::year_month_day
nmea::date
(
const
std::string_view& sv) {
93
std::string
s
(sv);
94
// NMEA returns two digits, and not all systems know what century we are in.
95
// Current solution: use the century defined in source code.
96
// alternative could be to convert to stream and use formatter to parse 2 digit year
97
// but that's a bit much for our use case
98
#define CENTURY (2000)
99
return
std::chrono::year_month_day{
100
std::chrono::year(std::stoi(
s
.substr(4)) +
CENTURY
),
101
std::chrono::month(std::stoi(
s
.substr(2, 2))),
102
std::chrono::day(std::stoi(
s
.substr(0, 2)))
103
};
104
#undef CENTURY
105
}
106
107
bool
nmea::valid
(
const
std::string_view& sv) {
108
assert(sv.starts_with(
'A'
) ||
109
sv.starts_with(
'V'
));
110
return
sv.starts_with(
'A'
);
111
}
112
113
quality
nmea::qual
(
const
std::string_view& sv) {
114
// the typecast from int to enem<unsigned int> below is confirmed
115
// to be safe in this case
116
// https://www.modernescpp.com/index.php/strongly-typed-enums/
117
return
static_cast<
quality
>
(std::stoi(std::string(sv)));
118
}
119
120
// $GPGLL,<Lat>,<N/S>,<Long>,<E/W>,<Timestamp>,<Status>,<mode indicator>*<checksum><cr><lf>
121
122
gll::gll_result
gll::from_data
(
const
std::string& data) {
123
unsigned
int
field = 0;
124
gll::gll_result
gll
;
125
for
(
const
auto
word : std::views::split(data,
delim
)) {
126
switch
(field) {
127
case
0:
// talker id
128
gll
.result.
source
=
nmea::talker
(std::string_view(word));
129
break
;
130
case
1:
// latitude
131
gll
.result.
lat
=
nmea::coord
(2, std::string_view(word));
132
break
;
133
case
2:
// latitude direction
134
if
(
nmea::dir
(std::string_view(word)) ==
direction::s
) {
135
gll
.result.
lat
=
gll
.result.
lat
* -1;
136
}
137
break
;
138
case
3:
// longitude
139
gll
.result.
lon
=
nmea::coord
(3, std::string_view(word));
140
break
;
141
case
4:
// longitude direction
142
if
(
nmea::dir
(std::string_view(word)) ==
direction::w
) {
143
gll
.result.
lon
=
gll
.result.
lon
* -1;
144
}
145
break
;
146
case
5:
// timestamp
147
gll
.result.
t
=
nmea::time
(std::string_view(word));
148
break
;
149
case
6:
// valid
150
gll
.result.
valid
=
nmea::valid
(std::string_view(word));
151
break
;
152
default
:
// skip 7
153
break
;
154
}
155
field++;
156
}
157
gll
.result.
success
= (field == 8);
// everything parsed
158
return
gll
;
159
}
160
161
// $GPGGA,<Timestamp>,<Lat>,<N/S>,<Long>,<E/W>,<GPSQual>,<Sats>,<HDOP>,<Alt>,<AltVal>,<GeoSep>,
162
// <GeoVal>,<DGPSAge>,<DGPSRef>*<checksum><cr><lf>
163
gga::gga_result
gga::from_data
(
const
std::string& data) {
164
unsigned
int
field = 0;
165
gga_result
gga
;
166
for
(
const
auto
word : std::views::split(data,
delim
)) {
167
switch
(field) {
168
case
0:
// talker id
169
gga
.result.
source
=
nmea::talker
(std::string_view(word));
170
break
;
171
case
1:
// timestamp
172
gga
.result.
t
=
nmea::time
(std::string_view(word));
173
break
;
174
case
2:
// latitude
175
gga
.result.
lat
=
nmea::coord
(2, std::string_view(word));
176
break
;
177
case
3:
// latitude direction
178
if
(
nmea::dir
(std::string_view(word)) ==
direction::s
) {
179
gga
.result.
lat
=
gga
.result.
lat
* -1;
180
}
181
break
;
182
case
4:
// longitude
183
gga
.result.
lon
=
nmea::coord
(3, std::string_view(word));
184
break
;
185
case
5:
// longitude direction
186
if
(
nmea::dir
(std::string_view(word)) ==
direction::w
) {
187
gga
.result.
lon
=
gga
.result.
lon
* -1;
188
}
189
break
;
190
case
6:
// qual
191
gga
.result.
qual
=
nmea::qual
(std::string_view(word));
192
break
;
193
case
7:
// sats
194
gga
.result.
sats
= std::stoi(std::string(std::string_view(word)));
195
break
;
196
case
9:
// altitude (in meters)
197
// a float (and a double) may not precisely represent the value in the message
198
gga
.result.
alt
= std::stof(std::string(std::string_view(word)),
nullptr
);
199
break
;
200
case
11:
// geoid separation (in meters)
201
// a float (and a double) may not precisely represent the value in the message
202
gga
.result.
geosep
= std::stof(std::string(std::string_view(word)),
nullptr
);
203
break
;
204
default
:
// skip 8, 10, 12 .. 15
205
break
;
206
}
207
field++;
208
}
209
gga
.result.
success
= (field == 15);
// everything parsed
210
return
gga
;
211
}
212
213
// $GNGSA,A,3,15,18,,,,,,,,,,,4.7,3.7,2.9*2D
214
// $GNGSA,A,3,73,65,81,,,,,,,,,,4.7,3.7,2.9*2E
215
gsa::gsa_result
gsa::from_data
(
const
std::string& data) {
216
unsigned
int
field = 0;
217
gsa_result
gsa
;
218
std::string_view v = std::string_view(data).substr(0, data.find(
'*'
));
219
for
(
const
auto
word : std::views::split(v,
delim
)) {
220
switch
(field) {
221
case
0:
// talker id
222
gsa
.result.
source
=
nmea::talker
(std::string_view(word));
223
break
;
224
case
3:
225
case
4:
226
case
5:
227
case
6:
228
case
7:
229
case
8:
230
case
9:
231
case
10:
232
case
11:
233
case
12:
234
case
13:
235
case
14:
236
if
(std::string_view(word).length() == 0) {
237
gsa
.result.
sats
[field - 3] = 0;
238
}
else
{
239
gsa
.result.
sats
[field - 3] =
240
std::stoi(std::string(std::string_view(word)));
241
}
242
break
;
243
case
15:
244
gsa
.result.
system_id
=
nmea::system
(std::string_view(word));
245
break
;
246
default
:
// skip 1,2, 16, 17
247
break
;
248
}
249
field++;
250
}
251
gsa
.result.
success
= (field == 18);
// everything parsed
252
return
gsa
;
253
}
254
255
// $GPGSV,3,1,11,13,79,310,,14,53,113,,05,51,214,,30,47,067,*72
256
// $GPGSV,3,2,11,15,45,295,24,22,44,145,,20,27,192,,07,16,064,*7A
257
// $GPGSV,3,3,11,18,16,298,25,24,08,249,,08,08,029,18,,,,*40
258
// $GLGSV,2,1,08,72,79,113,,74,77,084,,75,38,202,,65,37,317,28*68
259
// $GLGSV,2,2,08,73,34,040,35,71,28,130,,81,13,333,24,82,08,017,*68
260
gsv::gsv_result
gsv::from_data
(
const
std::string& data) {
261
unsigned
int
field = 0;
262
gsv_result
gsv
;
263
std::string_view v = std::string_view(data).substr(0, data.find(
'*'
));
264
for
(
const
auto
word : std::views::split(v,
delim
)) {
265
switch
(field) {
266
case
0:
// talker id
267
gsv
.result.
source
=
nmea::talker
(std::string_view(word));
268
break
;
269
case
4:
270
case
8:
271
case
12:
272
case
16:
273
if
(std::string_view(word).length() == 0) {
274
gsv
.result.
sats
[(field - 4) / 4 ].prn = 0;
275
}
else
{
276
gsv
.result.
sats
[(field - 4) / 4 ].prn =
277
std::stoi(std::string(std::string_view(word)));
278
}
279
break
;
280
case
5:
281
case
9:
282
case
13:
283
case
17:
284
if
(std::string_view(word).length() == 0) {
285
gsv
.result.
sats
[(field - 5) / 4 ].elev = 0;
286
}
else
{
287
gsv
.result.
sats
[(field - 5) / 4 ].elev =
288
std::stoi(std::string(std::string_view(word)));
289
}
290
break
;
291
case
6:
292
case
10:
293
case
14:
294
case
18:
295
if
(std::string_view(word).length() == 0) {
296
gsv
.result.
sats
[(field - 6) / 4 ].azim = 0;
297
}
else
{
298
gsv
.result.
sats
[(field - 6) / 4 ].azim =
299
std::stoi(std::string(std::string_view(word)));
300
}
301
break
;
302
case
7:
303
case
11:
304
case
15:
305
case
19:
306
if
(std::string_view(word).length() == 0) {
307
gsv
.result.
sats
[(field - 7) / 4 ].snr = 0;
308
}
else
{
309
gsv
.result.
sats
[(field - 7) / 4 ].snr =
310
std::stoi(std::string(std::string_view(word)));
311
}
312
break
;
313
default
:
// skip 1, 2, 3
314
break
;
315
}
316
field++;
317
}
318
gsv
.result.
success
= (field == 20);
// everything parsed
319
return
gsv
;
320
}
321
322
// $GPRMC,185427.150,V,5051.83778,N,00422.55809,E,,,240724,,,N*7F
323
rmc::rmc_result
rmc::from_data
(
const
std::string& data) {
324
unsigned
int
field = 0;
325
rmc_result
rmc
;
326
for
(
const
auto
word : std::views::split(data,
delim
)) {
327
switch
(field) {
328
case
0:
// talker id
329
rmc
.result.
source
=
nmea::talker
(std::string_view(word));
330
break
;
331
case
1:
// timestamp
332
rmc
.result.
t
=
nmea::time
(std::string_view(word));
333
break
;
334
case
2:
// valid
335
rmc
.result.
valid
=
nmea::valid
(std::string_view(word));
336
break
;
337
case
3:
// latitude
338
rmc
.result.
lat
=
nmea::coord
(2, std::string_view(word));
339
break
;
340
case
4:
// latitude direction
341
if
(
nmea::dir
(std::string_view(word)) ==
direction::s
) {
342
rmc
.result.
lat
=
rmc
.result.
lat
* -1;
343
}
344
break
;
345
case
5:
// longitude
346
rmc
.result.
lon
=
nmea::coord
(3, std::string_view(word));
347
break
;
348
case
6:
// longitude direction
349
if
(
nmea::dir
(std::string_view(word)) ==
direction::w
) {
350
rmc
.result.
lon
=
rmc
.result.
lon
* -1;
351
}
352
break
;
353
case
9:
// dqtestamp
354
rmc
.result.
d
=
nmea::date
(std::string_view(word));
355
break
;
356
default
:
// skip 7, 8, 10, 11, 12
357
break
;
358
}
359
field++;
360
}
361
rmc
.result.
success
= (field == 13);
// everything parsed
362
return
rmc
;
363
}
364
365
}
// namespace nmea
nmea::nmea::qual
static quality qual(const std::string_view &sv)
Definition
nmea.cpp:113
nmea::nmea::date
static std::chrono::year_month_day date(const std::string_view &sv)
Definition
nmea.cpp:92
nmea::nmea::valid
static bool valid(const std::string_view &sv)
Definition
nmea.cpp:107
nmea::nmea::time
static time_t time(const std::string_view &sv)
Definition
nmea.cpp:82
nmea::nmea::system
static talker_id system(const std::string_view &sv)
Definition
nmea.cpp:40
nmea::nmea::dir
static direction dir(const std::string_view &sv)
Definition
nmea.cpp:64
nmea::nmea::coord
static float coord(const unsigned int degrees_chars, const std::string_view &sv)
Definition
nmea.cpp:56
nmea::nmea::talker
static talker_id talker(const std::string_view &sv)
Definition
nmea.cpp:22
nmea
nmea
Definition
nmea.cpp:20
nmea::quality
quality
Definition
nmea_iface.cpp:40
nmea::time_t
std::chrono::hh_mm_ss< std::chrono::duration< long long, std::ratio< 1, 1000 > > > time_t
Definition
nmea_iface.cpp:21
nmea::direction
direction
Definition
nmea_iface.cpp:33
nmea::direction::s
@ s
Definition
nmea_iface.cpp:35
nmea::direction::n
@ n
Definition
nmea_iface.cpp:34
nmea::direction::e
@ e
Definition
nmea_iface.cpp:36
nmea::direction::w
@ w
Definition
nmea_iface.cpp:37
nmea::talker_id
talker_id
Definition
nmea_iface.cpp:23
nmea::talker_id::gps
@ gps
Definition
nmea_iface.cpp:25
nmea::talker_id::multiconstellation
@ multiconstellation
Definition
nmea_iface.cpp:30
nmea::talker_id::galileo
@ galileo
Definition
nmea_iface.cpp:27
nmea::talker_id::qzss
@ qzss
Definition
nmea_iface.cpp:29
nmea::talker_id::glonass
@ glonass
Definition
nmea_iface.cpp:26
nmea::talker_id::beidou
@ beidou
Definition
nmea_iface.cpp:28
delim
constexpr auto delim
Definition
nmea.cpp:18
CENTURY
#define CENTURY
nmea::gga
Definition
nmea_iface.cpp:83
nmea::gga::geosep
float geosep
Definition
nmea_iface.cpp:93
nmea::gga::qual
quality qual
Definition
nmea_iface.cpp:91
nmea::gga::success
bool success
Definition
nmea_iface.cpp:94
nmea::gga::source
talker_id source
Definition
nmea_iface.cpp:86
nmea::gga::alt
float alt
Definition
nmea_iface.cpp:92
nmea::gga::lon
float lon
Definition
nmea_iface.cpp:88
nmea::gga::lat
float lat
Definition
nmea_iface.cpp:87
nmea::gga::t
time_t t
Definition
nmea_iface.cpp:89
nmea::gga::sats
unsigned int sats
Definition
nmea_iface.cpp:90
nmea::gga::from_data
static gga_result from_data(const std::string &data)
Definition
nmea.cpp:163
nmea::gga::gga_result
nmea_result< gga > gga_result
Definition
nmea_iface.cpp:84
nmea::gll
Definition
nmea_iface.cpp:72
nmea::gll::valid
bool valid
Definition
nmea_iface.cpp:79
nmea::gll::source
talker_id source
Definition
nmea_iface.cpp:75
nmea::gll::lat
float lat
Definition
nmea_iface.cpp:76
nmea::gll::lon
float lon
Definition
nmea_iface.cpp:77
nmea::gll::gll_result
nmea_result< gll > gll_result
Definition
nmea_iface.cpp:73
nmea::gll::t
time_t t
Definition
nmea_iface.cpp:78
nmea::gll::from_data
static gll_result from_data(const std::string &data)
Definition
nmea.cpp:122
nmea::gll::success
bool success
Definition
nmea_iface.cpp:80
nmea::gsa
Definition
nmea_iface.cpp:99
nmea::gsa::source
talker_id source
Definition
nmea_iface.cpp:102
nmea::gsa::success
bool success
Definition
nmea_iface.cpp:105
nmea::gsa::gsa_result
nmea_result< gsa > gsa_result
Definition
nmea_iface.cpp:100
nmea::gsa::from_data
static gsa_result from_data(const std::string &data)
Definition
nmea.cpp:215
nmea::gsa::system_id
talker_id system_id
Definition
nmea_iface.cpp:103
nmea::gsa::sats
gsa_sat_array sats
Definition
nmea_iface.cpp:104
nmea::gsv
Definition
nmea_iface.cpp:117
nmea::gsv::source
talker_id source
Definition
nmea_iface.cpp:120
nmea::gsv::gsv_result
nmea_result< gsv > gsv_result
Definition
nmea_iface.cpp:118
nmea::gsv::success
bool success
Definition
nmea_iface.cpp:122
nmea::gsv::sats
gsv_sat_array sats
Definition
nmea_iface.cpp:121
nmea::gsv::from_data
static gsv_result from_data(const std::string &data)
Definition
nmea.cpp:260
nmea::rmc
Definition
nmea_iface.cpp:125
nmea::rmc::t
time_t t
Definition
nmea_iface.cpp:132
nmea::rmc::d
std::chrono::year_month_day d
Definition
nmea_iface.cpp:133
nmea::rmc::lat
float lat
Definition
nmea_iface.cpp:129
nmea::rmc::lon
float lon
Definition
nmea_iface.cpp:130
nmea::rmc::from_data
static rmc_result from_data(const std::string &data)
Definition
nmea.cpp:323
nmea::rmc::source
talker_id source
Definition
nmea_iface.cpp:128
nmea::rmc::success
bool success
Definition
nmea_iface.cpp:135
nmea::rmc::rmc_result
nmea_result< rmc > rmc_result
Definition
nmea_iface.cpp:126
nmea::rmc::valid
bool valid
Definition
nmea_iface.cpp:134
Generated by
1.17.0