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
;
141 bool gpsNewFrameNMEA(char c
)
143 static gpsDataNmea_t gps_Msg
;
146 static uint8_t param
= 0, offset
= 0, parity
= 0;
147 static char string
[15];
148 static uint8_t checksum_param
, gps_frame
= NO_FRAME
;
160 // Frame identification (accept all GPS talkers (GP: GPS, GL:Glonass, GN:combination, etc...))
161 gps_frame
= NO_FRAME
;
162 if (string
[0] == 'G' && string
[2] == 'G' && string
[3] == 'G' && string
[4] == 'A') {
163 gps_frame
= FRAME_GGA
;
165 else if (string
[0] == 'G' && string
[2] == 'R' && string
[3] == 'M' && string
[4] == 'C') {
166 gps_frame
= FRAME_RMC
;
169 // turn off this frame (do this only once a second)
170 static gtime_t lastGpsCmdSent
= 0;
171 if (string
[0] == 'G' && g_rtcTime
!= lastGpsCmdSent
) {
172 lastGpsCmdSent
= g_rtcTime
;
173 char cmd
[] = "$PUBX,40,GSV,0,0,0,0";
183 case FRAME_GGA
: //************* GPGGA FRAME parsing
186 gps_Msg
.latitude
= GPS_coord_to_degrees(string
);
189 if (string
[0] == 'S')
190 gps_Msg
.latitude
*= -1;
193 gps_Msg
.longitude
= GPS_coord_to_degrees(string
);
196 if (string
[0] == 'W')
197 gps_Msg
.longitude
*= -1;
200 if (string
[0] > '0') {
208 gps_Msg
.numSat
= grab_fields(string
, 0);
211 gps_Msg
.altitude
= grab_fields(string
, 0); // altitude in meters added by Mis
215 case FRAME_RMC
: //************* GPRMC FRAME parsing
218 gps_Msg
.time
= grab_fields(string
, 0);
221 if (string
[0] == 'A') {
229 gps_Msg
.speed
= ((grab_fields(string
, 1) * 5144L) / 1000L); // speed in cm/s added by Mis
232 gps_Msg
.groundCourse
= (grab_fields(string
, 1)); // ground course deg * 10
235 gps_Msg
.date
= grab_fields(string
, 0);
251 if (checksum_param
) { //parity checksum
252 uint8_t checksum
= 16 * ((string
[0] >= 'A') ? string
[0] - 'A' + 10 : string
[0] - '0') +
253 ((string
[1] >= 'A') ? string
[1] - 'A' + 10 : string
[1] - '0');
254 if (checksum
== parity
) {
255 gpsData
.packetCount
++;
259 gpsData
.fix
= gps_Msg
.fix
;
260 gpsData
.numSat
= gps_Msg
.numSat
;
262 __disable_irq(); // do the atomic update of lat/lon
263 gpsData
.latitude
= gps_Msg
.latitude
;
264 gpsData
.longitude
= gps_Msg
.longitude
;
265 gpsData
.altitude
= gps_Msg
.altitude
;
270 gpsData
.speed
= gps_Msg
.speed
;
271 gpsData
.groundCourse
= gps_Msg
.groundCourse
;
273 // set RTC clock if needed
274 if (g_eeGeneral
.adjustRTC
&& gps_Msg
.fix
) {
275 div_t qr
= div(gps_Msg
.date
, 100);
276 uint8_t year
= qr
.rem
;
277 qr
= div(qr
.quot
, 100);
278 uint8_t mon
= qr
.rem
;
279 uint8_t day
= qr
.quot
;
280 qr
= div(gps_Msg
.time
, 100);
281 uint8_t sec
= qr
.rem
;
282 qr
= div(qr
.quot
, 100);
283 uint8_t min
= qr
.rem
;
284 uint8_t hour
= qr
.quot
;
285 rtcAdjust(year
+2000, mon
, day
, hour
, min
, sec
);
291 gpsData
.errorCount
++;
298 string
[offset
++] = c
;
305 bool gpsNewFrame(uint8_t c
)
307 return gpsNewFrameNMEA(c
);
310 void gpsNewData(uint8_t c
)
312 if (!gpsNewFrame(c
)) {
320 while (gpsGetByte(&byte
)) {
325 char hex(uint8_t b
) {
326 return b
> 9 ? b
+ 'A' - 10 : b
+ '0';
329 void gpsSendFrame(const char * frame
)
331 // send given frame, add checksum and CRLF
333 TRACE_NOCRLF("gps> %s", frame
);
335 if (*frame
!= '$') parity
^= *frame
;
340 gpsSendByte(hex(parity
>> 4));
341 gpsSendByte(hex(parity
& 0x0F));
344 TRACE("*%02x", parity
);