1 /*
2 * This file is part of nmealib.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #ifndef __NMEALIB_INFO_H__
19 #define __NMEALIB_INFO_H__
20
21 #include <nmealib/util.h>
22 #include <stdbool.h>
23 #include <stdint.h>
24 #include <sys/time.h>
25
26 #ifdef __cplusplus
27 extern "C" {
28 #endif /* __cplusplus */
29
30 /*
31 * SIG
32 */
33
34 /**
35 * Enumeration for the signal names
36 *
37 * The values are used in the 'sig' field.
38 */
39 typedef enum _NmeaSignal {
40 NMEALIB_SIG_INVALID = 0u,
41 NMEALIB_SIG_FIRST = NMEALIB_SIG_INVALID,
42 NMEALIB_SIG_FIX = 1u,
43 NMEALIB_SIG_DIFFERENTIAL = 2u,
44 NMEALIB_SIG_SENSITIVE = 3u,
45 NMEALIB_SIG_RTKIN = 4u,
46 NMEALIB_SIG_FLOAT_RTK = 5u,
47 NMEALIB_SIG_ESTIMATED = 6u,
48 NMEALIB_SIG_MANUAL = 7u,
49 NMEALIB_SIG_SIMULATION = 8u,
50 NMEALIB_SIG_LAST = NMEALIB_SIG_SIMULATION
51 } NmeaSignal;
52
53 /**
54 * Convert a NMEALIB_SIG_* signal into a string
55 *
56 * @param sig The NMEALIB_SIG_* signal
57 * @return The corresponding string, or NULL when the signal is unknown
58 */
nmeaInfoSignalToString(NmeaSignal sig)59 static INLINE const char *nmeaInfoSignalToString(NmeaSignal sig) {
60 switch (sig) {
61 case NMEALIB_SIG_INVALID:
62 return "INVALID";
63
64 case NMEALIB_SIG_FIX:
65 return "FIX";
66
67 case NMEALIB_SIG_DIFFERENTIAL:
68 return "DIFFERENTIAL";
69
70 case NMEALIB_SIG_SENSITIVE:
71 return "SENSITIVE";
72
73 case NMEALIB_SIG_RTKIN:
74 return "REAL TIME KINEMATIC";
75
76 case NMEALIB_SIG_FLOAT_RTK:
77 return "FLOAT REAL TIME KINEMATIC";
78
79 case NMEALIB_SIG_ESTIMATED:
80 return "ESTIMATED (DEAD RECKONING)";
81
82 case NMEALIB_SIG_MANUAL:
83 return "MANUAL";
84
85 case NMEALIB_SIG_SIMULATION:
86 return "SIMULATION";
87
88 default:
89 return NULL;
90 }
91 }
92
93 /**
94 * Convert a mode character into the corresponding NMEALIB_SIG_* signal
95 *
96 * @param mode The mode character
97 * @return The corresponding NMEALIB_SIG_* signal, or NMEALIB_SIG_INVALID when the
98 * mode is unknown
99 */
100 NmeaSignal nmeaInfoModeToSignal(char mode);
101
102 /**
103 * Convert a NMEALIB_SIG_* signal into the corresponding mode character
104 *
105 * @param sig The NMEALIB_SIG_* signal
106 * @return The corresponding mode character, or 'N' when the NMEALIB_SIG_* signal
107 * is unknown
108 */
109 char nmeaInfoSignalToMode(NmeaSignal sig);
110
111 /*
112 * FIX
113 */
114
115 typedef enum _NmeaFix {
116 NMEALIB_FIX_BAD = 1u,
117 NMEALIB_FIX_FIRST = NMEALIB_FIX_BAD,
118 NMEALIB_FIX_2D = 2u,
119 NMEALIB_FIX_3D = 3u,
120 NMEALIB_FIX_LAST = NMEALIB_FIX_3D
121 } NmeaFix;
122
123 /**
124 * Convert a NMEALIB_FIX_* fix into a string
125 *
126 * @param fix The NMEALIB_FIX_* fix
127 * @return The corresponding string, or NULL when the NMEALIB_FIX_* fix is
128 * unknown
129 */
nmeaInfoFixToString(NmeaFix fix)130 static INLINE const char *nmeaInfoFixToString(NmeaFix fix) {
131 switch (fix) {
132 case NMEALIB_FIX_BAD:
133 return "BAD";
134
135 case NMEALIB_FIX_2D:
136 return "2D";
137
138 case NMEALIB_FIX_3D:
139 return "3D";
140
141 default:
142 return NULL;
143 }
144 }
145
146 /*
147 * Limits and defaults
148 */
149
150 /** The maximum number of satellites (must be a multiple of NMEALIB_GPGSV_MAX_SATS_PER_SENTENCE) */
151 #define NMEALIB_MAX_SATELLITES (72u)
152
153 /** The default latitude */
154 #define NMEALIB_LATITUDE_DEFAULT_NDEG (0.0)
155
156 /** The default longitude */
157 #define NMEALIB_LONGITUDE_DEFAULT_NDEG (0.0)
158
159 /**
160 * Date and time data
161 */
162 typedef struct _NmeaTime {
163 unsigned int year; /**< Years - [1900, 2089] */
164 unsigned int mon; /**< Months - [ 1, 12] */
165 unsigned int day; /**< Day of the month - [ 1, 31] */
166 unsigned int hour; /**< Hours since midnight - [ 0, 23] */
167 unsigned int min; /**< Minutes after the hour - [ 0, 59] */
168 unsigned int sec; /**< Seconds after the minute - [ 0, 60] (1 leap second) */
169 unsigned int hsec; /**< Hundredth part of second - [ 0, 99] */
170 } NmeaTime;
171
172 /**
173 * Parse a NMEA time into a NmeaTime structure (time only, no date).
174 *
175 * The format that is used (HHMMSS, HHMMSS.t, HHMMSS.hh or HHMMSS.mmm) is
176 * determined by the length of the string.
177 *
178 * @param s The time
179 * @param ntime The structure in which to store the parsed time
180 * @return True on success
181 */
182 bool nmeaTimeParseTime(const char *s, NmeaTime *ntime);
183
184 /**
185 * Parse a NMEA date into a NmeaTime structure (date only, no time).
186 *
187 * @param s The date (DDMMYY)
188 * @param date The structure in which to store the parsed date
189 * @return True on success
190 */
191 bool nmeaTimeParseDate(const char *s, NmeaTime *date);
192
193 /**
194 * Position data in decimal degrees or radians
195 */
196 typedef struct _NmeaPosition {
197 double lat; /**< Latitude */
198 double lon; /**< Longitude */
199 } NmeaPosition;
200
201 /**
202 * Information about satellite
203 */
204 typedef struct _NmeaSatellite {
205 unsigned int prn; /**< Satellite PRN number - [1, inf) */
206 int elevation; /**< Elevation, in degrees - [0, 90] */
207 unsigned int azimuth; /**< Azimuth, degrees from true north - [0, 359] */
208 unsigned int snr; /**< Signal-to-Noise-Ratio - [0, 99] */
209 } NmeaSatellite;
210
211 /**
212 * Information about all tracked satellites
213 */
214 typedef struct _NmeaSatellites {
215 unsigned int inUseCount; /**< The number of satellites in use (not those in view) */
216 unsigned int inUse[NMEALIB_MAX_SATELLITES]; /**< The PRNs of satellites in use (not those in view) */
217 unsigned int inViewCount; /**< The number of satellites in view */
218 NmeaSatellite inView[NMEALIB_MAX_SATELLITES]; /**< Satellites information (in view) */
219 } NmeaSatellites;
220
221 /**
222 * Information about progress on non-atomic sentences
223 */
224 typedef struct _NmeaProgress {
225 bool gpgsvInProgress; /**< true when gpgsv is in progress */
226 } NmeaProgress;
227
228 /**
229 * GPS information from all supported sentences
230 */
231 typedef struct _NmeaInfo {
232 uint32_t present; /**< Bit-mask specifying which fields are present */
233 uint32_t smask; /**< Bit-mask specifying from which sentences data has been obtained */
234 NmeaTime utc; /**< UTC of the position data */
235 NmeaSignal sig; /**< Signal quality, see NMEALIB_SIG_* signals */
236 NmeaFix fix; /**< Operating mode, see NMEALIB_FIX_* fixes */
237 double pdop; /**< Position Dilution Of Precision */
238 double hdop; /**< Horizontal Dilution Of Precision */
239 double vdop; /**< Vertical Dilution Of Precision */
240 double latitude; /**< Latitude, in NDEG: +/-[degree][min].[sec/60] */
241 double longitude; /**< Longitude, in NDEG: +/-[degree][min].[sec/60] */
242 double elevation; /**< Elevation above/below mean sea level (geoid), in meters */
243 double height; /**< Height of geoid (elevation) above WGS84 ellipsoid, in meters */
244 double speed; /**< Speed over the ground in kph */
245 double track; /**< Track angle in degrees true north */
246 double mtrack; /**< Magnetic Track angle in degrees true north */
247 double magvar; /**< Magnetic variation in degrees */
248 double dgpsAge; /**< Time since last DGPS update, in seconds */
249 unsigned int dgpsSid; /**< DGPS station ID number */
250 NmeaSatellites satellites; /**< Satellites information */
251 NmeaProgress progress; /**< Progress information */
252 bool metric; /**< When true then units are metric */
253 } NmeaInfo;
254
255 /**
256 * Enumeration for the fields names of a NmeaInfo structure
257 *
258 * The values are used in the 'present' bit-mask.
259 */
260 typedef enum _NmeaPresence {
261 NMEALIB_PRESENT_SMASK = (1u << 0), /* 0x00000001 */
262 NMEALIB_PRESENT_FIRST = NMEALIB_PRESENT_SMASK,
263 NMEALIB_PRESENT_UTCDATE = (1u << 1), /* 0x00000002 */
264 NMEALIB_PRESENT_UTCTIME = (1u << 2), /* 0x00000004 */
265 NMEALIB_PRESENT_SIG = (1u << 3), /* 0x00000008 */
266
267 NMEALIB_PRESENT_FIX = (1u << 4), /* 0x00000010 */
268 NMEALIB_PRESENT_PDOP = (1u << 5), /* 0x00000020 */
269 NMEALIB_PRESENT_HDOP = (1u << 6), /* 0x00000040 */
270 NMEALIB_PRESENT_VDOP = (1u << 7), /* 0x00000080 */
271
272 NMEALIB_PRESENT_LAT = (1u << 8), /* 0x00000100 */
273 NMEALIB_PRESENT_LON = (1u << 9), /* 0x00000200 */
274 NMEALIB_PRESENT_ELV = (1u << 10), /* 0x00000400 */
275 NMEALIB_PRESENT_SPEED = (1u << 11), /* 0x00000800 */
276
277 NMEALIB_PRESENT_TRACK = (1u << 12), /* 0x00001000 */
278 NMEALIB_PRESENT_MTRACK = (1u << 13), /* 0x00002000 */
279 NMEALIB_PRESENT_MAGVAR = (1u << 14), /* 0x00004000 */
280 NMEALIB_PRESENT_SATINUSECOUNT = (1u << 15), /* 0x00008000 */
281
282 NMEALIB_PRESENT_SATINUSE = (1u << 16), /* 0x00010000 */
283 NMEALIB_PRESENT_SATINVIEWCOUNT = (1u << 17), /* 0x00020000 */
284 NMEALIB_PRESENT_SATINVIEW = (1u << 18), /* 0x00040000 */
285 NMEALIB_PRESENT_HEIGHT = (1u << 19), /* 0x00080000 */
286
287 NMEALIB_PRESENT_DGPSAGE = (1u << 20), /* 0x00100000 */
288 NMEALIB_PRESENT_DGPSSID = (1u << 21), /* 0x00200000 */
289
290 NMEALIB_PRESENT_LAST = NMEALIB_PRESENT_DGPSSID
291 } NmeaPresence;
292
293 /** The bit-mask of all supported field name bits */
294 #define NMEALIB_INFO_PRESENT_MASK (NMEALIB_PRESENT_LAST | (NMEALIB_PRESENT_LAST - 1))
295
296 /**
297 * Convert a NmeaPresence into a string
298 *
299 * @param field The NmeaPresence
300 * @return The corresponding string, or NULL when the NmeaPresence is unknown
301 */
nmeaInfoFieldToString(NmeaPresence field)302 static INLINE const char *nmeaInfoFieldToString(NmeaPresence field) {
303 switch (field) {
304 case NMEALIB_PRESENT_SMASK:
305 return "SMASK";
306
307 case NMEALIB_PRESENT_UTCDATE:
308 return "UTCDATE";
309
310 case NMEALIB_PRESENT_UTCTIME:
311 return "UTCTIME";
312
313 case NMEALIB_PRESENT_SIG:
314 return "SIG";
315
316 case NMEALIB_PRESENT_FIX:
317 return "FIX";
318
319 case NMEALIB_PRESENT_PDOP:
320 return "PDOP";
321
322 case NMEALIB_PRESENT_HDOP:
323 return "HDOP";
324
325 case NMEALIB_PRESENT_VDOP:
326 return "VDOP";
327
328 case NMEALIB_PRESENT_LAT:
329 return "LAT";
330
331 case NMEALIB_PRESENT_LON:
332 return "LON";
333
334 case NMEALIB_PRESENT_ELV:
335 return "ELV";
336
337 case NMEALIB_PRESENT_HEIGHT:
338 return "HEIGHT";
339
340 case NMEALIB_PRESENT_SPEED:
341 return "SPEED";
342
343 case NMEALIB_PRESENT_TRACK:
344 return "TRACK";
345
346 case NMEALIB_PRESENT_MTRACK:
347 return "MTRACK";
348
349 case NMEALIB_PRESENT_MAGVAR:
350 return "MAGVAR";
351
352 case NMEALIB_PRESENT_SATINUSECOUNT:
353 return "SATINUSECOUNT";
354
355 case NMEALIB_PRESENT_SATINUSE:
356 return "SATINUSE";
357
358 case NMEALIB_PRESENT_SATINVIEWCOUNT:
359 return "SATINVIEWCOUNT";
360
361 case NMEALIB_PRESENT_SATINVIEW:
362 return "SATINVIEW";
363
364 case NMEALIB_PRESENT_DGPSAGE:
365 return "DGPSAGE";
366
367 case NMEALIB_PRESENT_DGPSSID:
368 return "DGPSSID";
369
370 default:
371 return NULL;
372 }
373 }
374
375 /**
376 * Determine if a 'present' bit-mask indicates presence of all of the
377 * indicated NmeaPresence field names
378 *
379 * @param present The 'present' field
380 * @param fieldName The NmeaPresence to check for presence
381 * @return True when all of the NmeaPresence field names are present
382 */
nmeaInfoIsPresentAll(uint32_t present,NmeaPresence fieldName)383 static INLINE bool nmeaInfoIsPresentAll(uint32_t present, NmeaPresence fieldName) {
384 return ((present & fieldName) == fieldName);
385 }
386
387 /**
388 * Determine if a 'present' bit-mask indicates presence of any of the
389 * indicated NmeaPresence field names
390 *
391 * @param present The 'present' field
392 * @param fieldName The NmeaPresence bit-mask to check for presence
393 * @return True when any of the NmeaPresence field names are present
394 */
nmeaInfoIsPresentAny(uint32_t present,NmeaPresence fieldName)395 static INLINE bool nmeaInfoIsPresentAny(uint32_t present, NmeaPresence fieldName) {
396 return ((present & fieldName) != 0);
397 }
398
399 /**
400 * Adjust a 'present' bit-mask to indicate presence of a certain
401 * NmeaPresence
402 *
403 * @param present The 'present' field
404 * @param fieldName The NmeaPresence to indicate presence of
405 */
nmeaInfoSetPresent(uint32_t * present,NmeaPresence fieldName)406 static INLINE void nmeaInfoSetPresent(uint32_t *present, NmeaPresence fieldName) {
407 if (present) {
408 *present |= fieldName;
409 }
410 }
411
412 /**
413 * Adjust a 'present' bit-mask to indicate absence of a certain NmeaPresence
414 *
415 * @param present The 'present' field
416 * @param fieldName The NmeaPresence to absence presence of
417 */
nmeaInfoUnsetPresent(uint32_t * present,NmeaPresence fieldName)418 static INLINE void nmeaInfoUnsetPresent(uint32_t *present, NmeaPresence fieldName) {
419 if (present) {
420 *present &= ~fieldName;
421 }
422 }
423
424 /**
425 * Reset the time to now
426 *
427 * @param utc The time
428 * @param present The 'present' field (when non-NULL then the UTCDATE and
429 * UTCTIME flags are set in it)
430 * @param timeval If non-NULL then use this provided time, otherwise the
431 * 'gettimeofday' c-library function is used to obtain it
432 */
433 void nmeaTimeSet(NmeaTime *utc, uint32_t *present, struct timeval *timeval);
434
435 /**
436 * Clear an info structure.
437 *
438 * Sets up the signal as NMEALIB_SIG_INVALID, the FIX as
439 * NMEALIB_FIX_BAD, and signals presence of these fields.
440 *
441 * Resets all other fields to 0.
442 *
443 * @param info The info structure
444 */
445 void nmeaInfoClear(NmeaInfo *info);
446
447 /**
448 * Sanitise the NMEA info, make sure that:
449 * - all information is in the original units
450 * - utc date is set to the current date when not present
451 * - utc time is set to the current time when not present
452 * - sig is in the range [NMEALIB_SIG_FIRST, NMEALIB_SIG_LAST],
453 * if this is not the case then sig is set to NMEALIB_SIG_INVALID
454 * - fix is in the range [NMEALIB_FIX_FIRST, NMEALIB_FIX_LAST],
455 * if this is not the case then fix is set to NMEALIB_FIX_BAD
456 * - DOPs are positive
457 * - latitude is in the range [-9000, 9000]
458 * - longitude is in the range [-18000, 18000]
459 * - speed is positive
460 * - track is in the range [0, 360>
461 * - mtrack is in the range [0, 360>
462 * - magvar is in the range [0, 360>
463 * - dgpsAge is positive
464 * - dgpsSid is positive
465 * - satinfo:
466 * - inuseCount is positive
467 * - each inUse satellite has a positive PRN
468 * - inUse satellites are compacted (satellites with a zero PRN last)
469 * - inViewCount is positive
470 * - inView (only when GPGSV is not 'in progress'):
471 * - prn is positive
472 * - elevation is in the range [-90, 90]
473 * - azimuth is in the range [0, 359]
474 * - snr is in the range [0, 99]
475 *
476 * Fields are reset to their defaults when not signalled as being present.
477 *
478 * @param info The NMEA info structure to sanitise
479 */
480 void nmeaInfoSanitise(NmeaInfo *info);
481
482 /**
483 * Converts the position fields to degrees and DOP fields to meters so that
484 * all fields use normal metric units or original units.
485 *
486 * If the NmeaInfo information is already in the requested format then
487 * this function does nothing.
488 *
489 * @param info The NmeaInfo
490 * @param toMetric Convert to metric units (from original units) when true,
491 * convert to original units (from metric units) when false
492 */
493 void nmeaInfoUnitConversion(NmeaInfo *info, bool toMetric);
494
495 /**
496 * Compare 2 satellite PRNs and put zeroes last
497 *
498 * @param p1 The first satellite PRN
499 * @param p2 The second satellite PRN
500 * @return 0 when both are equal, a negative value when PRN1 < PRN2, a
501 * positive value otherwise
502 */
503 int nmeaQsortPRNCompare(const void *p1, const void *p2);
504
505 /**
506 * Compact 2 satellite PRNs (do not reorder) and put zeroes last
507 *
508 * @param p1 The first satellite PRN
509 * @param p2 The second satellite PRN
510 * @return 0 when both are non-zero or are equal, a negative value when
511 * PRN1 < PRN2, a positive value otherwise
512 */
513 int nmeaQsortPRNCompact(const void *p1, const void *p2);
514
515 /**
516 * Compare 2 satellite PRNs and put zeroes last
517 *
518 * @param s1 The first satellite
519 * @param s2 The second satellite
520 * @return 0 when both are equal, a negative value when PRN1 < PRN2, a
521 * positive value otherwise
522 */
523 int nmeaQsortSatelliteCompare(const void *s1, const void *s2);
524
525 /**
526 * Compact 2 satellite PRNs (do not reorder) and put zeroes last
527 *
528 * @param s1 The first satellite
529 * @param s2 The second satellite
530 * @return 0 when both are non-zero or are equal, a negative value when
531 * PRN1 < PRN2, a positive value otherwise
532 */
533 int nmeaQsortSatelliteCompact(const void *s1, const void *s2);
534
535 #ifdef __cplusplus
536 }
537 #endif /* __cplusplus */
538
539 #endif /* __NMEALIB_INFO_H__ */
540