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 #include <nmealib/validate.h>
19 
20 #include <nmealib/context.h>
21 
22 /** Invalid NMEA character: non-ASCII */
23 static const NmeaInvalidCharacter nmealibInvalidNonAsciiCharsName = {
24     .character = '*', //
25     .description = "non-ASCII character" //
26     };
27 
28 /** Invalid NMEA character/description pairs */
29 static const NmeaInvalidCharacter nmealibInvalidCharacters[] = {
30     {
31         .character = '$', //
32         .description = "sentence delimiter" //
33     },
34     {
35         .character = '*', //
36         .description = "checksum field delimiter" //
37     },
38     {
39         .character = '!', //
40         .description = "exclamation mark" //
41     },
42     {
43         .character = '\\', //
44         .description = "backslash" //
45     },
46     {
47         .character = '^', //
48         .description = "power" //
49     },
50     {
51         .character = '~', //
52         .description = "tilde" //
53     },
54     {
55         .character = '\0', //
56         .description = NULL //
57     }//
58 };
59 
nmeaValidateIsInvalidCharacter(const char c)60 const NmeaInvalidCharacter *nmeaValidateIsInvalidCharacter(const char c) {
61   size_t i = 0;
62 
63   if ((c < 32) //
64       || (c > 126)) {
65     return &nmealibInvalidNonAsciiCharsName;
66   }
67 
68   while (nmealibInvalidCharacters[i].description) {
69     if (c == nmealibInvalidCharacters[i].character) {
70       return &nmealibInvalidCharacters[i];
71     }
72 
73     i++;
74   }
75 
76   return NULL;
77 }
78 
nmeaValidateSentenceHasInvalidCharacters(const char * s,const size_t sz)79 const NmeaInvalidCharacter *nmeaValidateSentenceHasInvalidCharacters(const char *s, const size_t sz) {
80   size_t i = 0;
81 
82   if (!s //
83       || !sz) {
84     return NULL;
85   }
86 
87   for (i = 0; i < sz; i++) {
88     const NmeaInvalidCharacter *invalid = nmeaValidateIsInvalidCharacter(s[i]);
89     if (invalid) {
90       return invalid;
91     }
92   }
93 
94   return NULL;
95 }
96 
nmeaValidateTime(const NmeaTime * t,const char * prefix,const char * s)97 bool nmeaValidateTime(const NmeaTime *t, const char *prefix, const char *s) {
98   if (!t) {
99     return false;
100   }
101 
102   if ((t->hour > 23) //
103       || (t->min > 59) //
104       || (t->sec > 60) //
105       || (t->hsec > 99)) {
106     nmeaContextError("%s parse error: invalid time '%02u:%02u:%02u.%03u' (hh:mm:ss.mmm) in '%s'", prefix, t->hour,
107         t->min, t->sec, t->hsec * 10, s);
108     return false;
109   }
110 
111   return true;
112 }
113 
nmeaValidateDate(const NmeaTime * t,const char * prefix,const char * s)114 bool nmeaValidateDate(const NmeaTime *t, const char *prefix, const char *s) {
115   if (!t) {
116     return false;
117   }
118 
119   if ((t->year < 1900) //
120       || (t->year > 2089) //
121       || (t->mon < 1) //
122       || (t->mon > 12) //
123       || (t->day < 1) //
124       || (t->day > 31)) {
125     nmeaContextError("%s parse error: invalid date '%02u-%02u-%04u' (dd-mm-yyyy) in '%s'", prefix, t->day, t->mon,
126         t->year, s);
127     return false;
128   }
129 
130   return true;
131 }
132 
nmeaValidateNSEW(char c,const bool ns,const char * prefix,const char * s)133 bool nmeaValidateNSEW(char c, const bool ns, const char *prefix, const char *s) {
134   char cu[] = {
135       0,
136       0,
137       0 };
138 
139   if (c) {
140     cu[0] = c;
141   } else {
142     cu[0] = '\\';
143     cu[1] = '0';
144   }
145 
146   if (ns) {
147     if ((c != 'N') //
148         && (c != 'S')) {
149       nmeaContextError("%s parse error: invalid North/South '%s' in '%s'", prefix, cu, s);
150       return false;
151     }
152   } else {
153     if ((c != 'E') //
154         && (c != 'W')) {
155       nmeaContextError("%s parse error: invalid East/West '%s' in '%s'", prefix, cu, s);
156       return false;
157     }
158   }
159 
160   return true;
161 }
162 
nmeaValidateFix(NmeaFix fix,const char * prefix,const char * s)163 bool nmeaValidateFix(NmeaFix fix, const char *prefix, const char *s) {
164   if ((fix < NMEALIB_FIX_FIRST) //
165       || (fix > NMEALIB_FIX_LAST)) {
166     nmeaContextError("%s parse error: invalid fix %d, expected [%d, %d] in '%s'", prefix, fix, NMEALIB_FIX_FIRST,
167         NMEALIB_FIX_LAST, s);
168     return false;
169   }
170 
171   return true;
172 }
173 
nmeaValidateSignal(NmeaSignal sig,const char * prefix,const char * s)174 bool nmeaValidateSignal(NmeaSignal sig, const char *prefix, const char *s) {
175   if (sig > NMEALIB_SIG_LAST) {
176     nmeaContextError("%s parse error: invalid signal %d, expected [%d, %d] in '%s'", prefix, sig, NMEALIB_SIG_FIRST,
177         NMEALIB_SIG_LAST, s);
178     return false;
179   }
180 
181   return true;
182 }
183 
nmeaValidateMode(char c,const char * prefix,const char * s)184 bool nmeaValidateMode(char c, const char *prefix, const char *s) {
185   if (!c) {
186     return false;
187   }
188 
189   if ((c != 'N') //
190       && (c != 'A') //
191       && (c != 'D') //
192       && (c != 'P') //
193       && (c != 'R') //
194       && (c != 'F') //
195       && (c != 'E') //
196       && (c != 'M') //
197       && (c != 'S')) {
198     nmeaContextError("%s parse error: invalid mode '%c' in '%s'", prefix, c, s);
199     return false;
200   }
201 
202   return true;
203 }
204 
nmeaValidateSatellite(NmeaSatellite * sat,const char * prefix,const char * s)205 bool nmeaValidateSatellite(NmeaSatellite *sat, const char *prefix, const char *s) {
206   if (!sat) {
207     return false;
208   }
209 
210   if ((sat->elevation < -180) //
211       || (sat->elevation > 180)) {
212     nmeaContextError("%s parse error: invalid satellite elevation %d in '%s'", prefix, sat->elevation, s);
213     return false;
214   }
215 
216   if (sat->azimuth > 359) {
217     nmeaContextError("%s parse error: invalid satellite azimuth %u in '%s'", prefix, sat->azimuth, s);
218     return false;
219   }
220 
221   if (sat->snr > 99) {
222     nmeaContextError("%s parse error: invalid satellite signal %u in '%s'", prefix, sat->snr, s);
223     return false;
224   }
225 
226   return true;
227 }
228