5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
22 * This file is based on code from Cleanflight project
23 * https://github.com/cleanflight/cleanflight
25 * Cleanflight is free software: you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation, either version 3 of the License, or
28 * (at your option) any later version.
30 * Cleanflight is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public License
36 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
44 /* This is a light implementation of a GPS frame decoding
45 This should work with most of modern GPS devices configured to output 5 frames.
46 It assumes there are some NMEA GGA frames to decode on the serial bus
47 Now verifies checksum correctly before applying data
49 Here we use only the following data :
52 - GPS fix is/is not ok
53 - GPS num sat (4 is enough to be +/- reliable)
55 - GPS altitude (for OSD displaying)
56 - GPS speed (for OSD displaying)
63 #define DIGIT_TO_VAL(_x) (_x - '0')
65 uint32_t GPS_coord_to_degrees(const char * coordinateString
)
67 const char * fieldSeparator
, * remainingString
;
68 uint8_t degrees
= 0, minutes
= 0;
69 uint16_t fractionalMinutes
= 0;
72 // scan for decimal point or end of field
73 for (fieldSeparator
= coordinateString
; isdigit((unsigned char) *fieldSeparator
); fieldSeparator
++) {
74 if (fieldSeparator
>= coordinateString
+ 15)
75 return 0; // stop potential fail
77 remainingString
= coordinateString
;
80 while ((fieldSeparator
- remainingString
) > 2) {
83 degrees
+= DIGIT_TO_VAL(*remainingString
++);
86 while (fieldSeparator
> remainingString
) {
89 minutes
+= DIGIT_TO_VAL(*remainingString
++);
91 // convert fractional minutes
92 // expect up to four digits, result is in
93 // ten-thousandths of a minute
94 if (*fieldSeparator
== '.') {
95 remainingString
= fieldSeparator
+ 1;
96 for (digitIndex
= 0; digitIndex
< 4; digitIndex
++) {
97 fractionalMinutes
*= 10;
98 if (isdigit((unsigned char) *remainingString
))
99 fractionalMinutes
+= *remainingString
++ - '0';
102 // TODO return degrees * 10000000UL + (minutes * 1000000UL + fractionalMinutes * 100UL) / 6;
103 return degrees
* 1000000UL + (minutes
* 100000UL + fractionalMinutes
* 10UL) / 6;
107 uint32_t grab_fields(char * src
, uint8_t mult
)
111 for (i
= 0; src
[i
] != 0; i
++) {
120 if (src
[i
] >= '0' && src
[i
] <= '9')
123 return 0; // out of bounds
128 typedef struct gpsDataNmea_s
136 uint16_t groundCourse
;
142 bool gpsNewFrameNMEA(char c
)
144 static gpsDataNmea_t gps_Msg
;
147 static uint8_t param
= 0, offset
= 0, parity
= 0;
148 static char string
[15];
149 static uint8_t checksum_param
, gps_frame
= NO_FRAME
;
161 // Frame identification (accept all GPS talkers (GP: GPS, GL:Glonass, GN:combination, etc...))
162 gps_frame
= NO_FRAME
;
163 if (string
[0] == 'G' && string
[2] == 'G' && string
[3] == 'G' && string
[4] == 'A') {
164 gps_frame
= FRAME_GGA
;
166 else if (string
[0] == 'G' && string
[2] == 'R' && string
[3] == 'M' && string
[4] == 'C') {
167 gps_frame
= FRAME_RMC
;
170 // turn off this frame (do this only once a second)
171 static gtime_t lastGpsCmdSent
= 0;
172 if (string
[0] == 'G' && g_rtcTime
!= lastGpsCmdSent
) {
173 lastGpsCmdSent
= g_rtcTime
;
174 char cmd
[] = "$PUBX,40,GSV,0,0,0,0";
184 case FRAME_GGA
: //************* GPGGA FRAME parsing
187 gps_Msg
.latitude
= GPS_coord_to_degrees(string
);
190 if (string
[0] == 'S')
191 gps_Msg
.latitude
*= -1;
194 gps_Msg
.longitude
= GPS_coord_to_degrees(string
);
197 if (string
[0] == 'W')
198 gps_Msg
.longitude
*= -1;
201 if (string
[0] > '0') {
209 gps_Msg
.numSat
= grab_fields(string
, 0);
212 gps_Msg
.hdop
= grab_fields(string
, 1) * 10;
215 gps_Msg
.altitude
= grab_fields(string
, 0); // altitude in meters added by Mis
219 case FRAME_RMC
: //************* GPRMC FRAME parsing
222 gps_Msg
.time
= grab_fields(string
, 0);
225 if (string
[0] == 'A') {
233 gps_Msg
.speed
= ((grab_fields(string
, 1) * 5144L) / 1000L); // speed in cm/s added by Mis
236 gps_Msg
.groundCourse
= (grab_fields(string
, 1)); // ground course deg * 10
239 gps_Msg
.date
= grab_fields(string
, 0);
255 if (checksum_param
) { //parity checksum
256 uint8_t checksum
= 16 * ((string
[0] >= 'A') ? string
[0] - 'A' + 10 : string
[0] - '0') +
257 ((string
[1] >= 'A') ? string
[1] - 'A' + 10 : string
[1] - '0');
258 if (checksum
== parity
) {
259 gpsData
.packetCount
++;
263 gpsData
.fix
= gps_Msg
.fix
;
264 gpsData
.numSat
= gps_Msg
.numSat
;
265 gpsData
.hdop
= gps_Msg
.hdop
;
267 __disable_irq(); // do the atomic update of lat/lon
268 gpsData
.latitude
= gps_Msg
.latitude
;
269 gpsData
.longitude
= gps_Msg
.longitude
;
270 gpsData
.altitude
= gps_Msg
.altitude
;
275 gpsData
.speed
= gps_Msg
.speed
;
276 gpsData
.groundCourse
= gps_Msg
.groundCourse
;
278 // set RTC clock if needed
279 if (g_eeGeneral
.adjustRTC
&& gps_Msg
.fix
) {
280 div_t qr
= div(gps_Msg
.date
, 100);
281 uint8_t year
= qr
.rem
;
282 qr
= div(qr
.quot
, 100);
283 uint8_t mon
= qr
.rem
;
284 uint8_t day
= qr
.quot
;
285 qr
= div(gps_Msg
.time
, 100);
286 uint8_t sec
= qr
.rem
;
287 qr
= div(qr
.quot
, 100);
288 uint8_t min
= qr
.rem
;
289 uint8_t hour
= qr
.quot
;
290 rtcAdjust(year
+2000, mon
, day
, hour
, min
, sec
);
296 gpsData
.errorCount
++;
303 string
[offset
++] = c
;
310 bool gpsNewFrame(uint8_t c
)
312 return gpsNewFrameNMEA(c
);
315 void gpsNewData(uint8_t c
)
317 if (!gpsNewFrame(c
)) {
325 while (gpsGetByte(&byte
)) {
330 char hex(uint8_t b
) {
331 return b
> 9 ? b
+ 'A' - 10 : b
+ '0';
334 void gpsSendFrame(const char * frame
)
336 // send given frame, add checksum and CRLF
338 TRACE_NOCRLF("gps> %s", frame
);
340 if (*frame
!= '$') parity
^= *frame
;
345 gpsSendByte(hex(parity
>> 4));
346 gpsSendByte(hex(parity
& 0x0F));
349 TRACE("*%02x", parity
);