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