|
38 | 38 | */
|
39 | 39 |
|
40 | 40 | #include "opentx.h"
|
| 41 | +#include "gps.h" |
| 42 | +#include "gps_nmea.h" |
| 43 | +#include "gps_ubx.h" |
41 | 44 | #include <ctype.h>
|
42 | 45 |
|
43 | 46 | gpsdata_t gpsData;
|
44 |
| - |
45 |
| -/* This is a light implementation of a GPS frame decoding |
46 |
| - This should work with most of modern GPS devices configured to output 5 frames. |
47 |
| - It assumes there are some NMEA GGA frames to decode on the serial bus |
48 |
| - Now verifies checksum correctly before applying data |
49 |
| -
|
50 |
| - Here we use only the following data : |
51 |
| - - latitude |
52 |
| - - longitude |
53 |
| - - GPS fix is/is not ok |
54 |
| - - GPS num sat (4 is enough to be +/- reliable) |
55 |
| - // added by Mis |
56 |
| - - GPS altitude (for OSD displaying) |
57 |
| - - GPS speed (for OSD displaying) |
58 |
| -*/ |
59 |
| - |
60 |
| -#define NO_FRAME 0 |
61 |
| -#define FRAME_GGA 1 |
62 |
| -#define FRAME_RMC 2 |
| 47 | +static int gpsProtocol = -1; |
| 48 | +const etx_serial_driver_t* gpsSerialDrv = nullptr; |
| 49 | +void* gpsSerialCtx = nullptr; |
63 | 50 |
|
64 | 51 | #define DIGIT_TO_VAL(_x) (_x - '0')
|
65 | 52 |
|
@@ -104,279 +91,118 @@ uint32_t GPS_coord_to_degrees(const char * coordinateString)
|
104 | 91 | return degrees * 1000000UL + (minutes * 100000UL + fractionalMinutes * 10UL) / 6;
|
105 | 92 | }
|
106 | 93 |
|
107 |
| -// helper functions |
108 |
| -uint32_t grab_fields(char * src, uint8_t mult) |
| 94 | +static void changeBaudrate() |
109 | 95 | {
|
110 |
| - uint32_t i; |
111 |
| - uint32_t tmp = 0; |
112 |
| - for (i = 0; src[i] != 0; i++) { |
113 |
| - if (src[i] == '.') { |
114 |
| - i++; |
115 |
| - if (mult == 0) |
116 |
| - break; |
117 |
| - else |
118 |
| - src[i + mult] = 0; |
119 |
| - } |
120 |
| - tmp *= 10; |
121 |
| - if (src[i] >= '0' && src[i] <= '9') |
122 |
| - tmp += src[i] - '0'; |
123 |
| - if (i >= 15) |
124 |
| - return 0; // out of bounds |
125 |
| - } |
126 |
| - return tmp; |
127 |
| -} |
| 96 | + const int baudrates_count = 5; |
| 97 | + const uint32_t baudrates[] = {9600, 57600, 115200, 19200, 38400}; |
| 98 | + static uint8_t current_rate = 0; |
128 | 99 |
|
129 |
| -typedef struct gpsDataNmea_s |
130 |
| -{ |
131 |
| - uint8_t fix; |
132 |
| - int32_t latitude; |
133 |
| - int32_t longitude; |
134 |
| - uint8_t numSat; |
135 |
| - uint16_t altitude; |
136 |
| - uint16_t speed; |
137 |
| - uint16_t groundCourse; |
138 |
| - uint16_t hdop; |
139 |
| - uint32_t date; |
140 |
| - uint32_t time; |
141 |
| -} gpsDataNmea_t; |
| 100 | + auto setBaudrate = gpsSerialDrv->setBaudrate; |
| 101 | + if (setBaudrate == nullptr) return; |
| 102 | + setBaudrate(gpsSerialCtx, baudrates[++current_rate % baudrates_count]); |
| 103 | +} |
142 | 104 |
|
143 |
| -bool gpsNewFrameNMEA(char c) |
| 105 | +static void autodetectProtocol(uint8_t c) |
144 | 106 | {
|
145 |
| - static gpsDataNmea_t gps_Msg; |
146 |
| - |
147 |
| - uint8_t frameOK = 0; |
148 |
| - static uint8_t param = 0, offset = 0, parity = 0; |
149 |
| - static char string[15]; |
150 |
| - static uint8_t checksum_param, gps_frame = NO_FRAME; |
151 |
| - |
152 |
| - switch (c) { |
153 |
| - case '$': |
154 |
| - param = 0; |
155 |
| - offset = 0; |
156 |
| - parity = 0; |
157 |
| - break; |
158 |
| - case ',': |
159 |
| - case '*': |
160 |
| - string[offset] = 0; |
161 |
| - if (param == 0) { |
162 |
| - // Frame identification (accept all GPS talkers (GP: GPS, GL:Glonass, GN:combination, etc...)) |
163 |
| - gps_frame = NO_FRAME; |
164 |
| - if (string[0] == 'G' && string[2] == 'G' && string[3] == 'G' && string[4] == 'A') { |
165 |
| - gps_frame = FRAME_GGA; |
166 |
| - } |
167 |
| - else if (string[0] == 'G' && string[2] == 'R' && string[3] == 'M' && string[4] == 'C') { |
168 |
| - gps_frame = FRAME_RMC; |
169 |
| - } |
170 |
| - else { |
171 |
| - // turn off this frame (do this only once a second) |
172 |
| - static gtime_t lastGpsCmdSent = 0; |
173 |
| - if (string[0] == 'G' && g_rtcTime != lastGpsCmdSent) { |
174 |
| - lastGpsCmdSent = g_rtcTime; |
175 |
| - char cmd[] = "$PUBX,40,GSV,0,0,0,0"; |
176 |
| - cmd[9] = string[2]; |
177 |
| - cmd[10] = string[3]; |
178 |
| - cmd[11] = string[4]; |
179 |
| - gpsSendFrame(cmd); |
180 |
| - } |
181 |
| - } |
| 107 | + static tmr10ms_t time; |
| 108 | + static uint8_t state = 0; |
| 109 | + |
| 110 | + switch (state) { |
| 111 | + case 0: // Init |
| 112 | + time = get_tmr10ms(); |
| 113 | + state = 1; |
| 114 | + case 1: // Wait for a valid packet |
| 115 | + if (gpsNewFrameNMEA(c)) { |
| 116 | + gpsProtocol = GPS_PROTOCOL_NMEA; |
| 117 | + state = 0; |
| 118 | + return; |
182 | 119 | }
|
183 | 120 |
|
184 |
| - switch (gps_frame) { |
185 |
| - case FRAME_GGA: //************* GPGGA FRAME parsing |
186 |
| - switch (param) { |
187 |
| - case 2: |
188 |
| - gps_Msg.latitude = GPS_coord_to_degrees(string); |
189 |
| - break; |
190 |
| - case 3: |
191 |
| - if (string[0] == 'S') |
192 |
| - gps_Msg.latitude *= -1; |
193 |
| - break; |
194 |
| - case 4: |
195 |
| - gps_Msg.longitude = GPS_coord_to_degrees(string); |
196 |
| - break; |
197 |
| - case 5: |
198 |
| - if (string[0] == 'W') |
199 |
| - gps_Msg.longitude *= -1; |
200 |
| - break; |
201 |
| - case 6: |
202 |
| - if (string[0] > '0') { |
203 |
| - gps_Msg.fix = 1; |
204 |
| - } |
205 |
| - else { |
206 |
| - gps_Msg.fix = 0; |
207 |
| - } |
208 |
| - break; |
209 |
| - case 7: |
210 |
| - gps_Msg.numSat = grab_fields(string, 0); |
211 |
| - break; |
212 |
| - case 8: |
213 |
| - gps_Msg.hdop = grab_fields(string, 1) * 10; |
214 |
| - break; |
215 |
| - case 9: |
216 |
| - gps_Msg.altitude = grab_fields(string, 0); // altitude in meters added by Mis |
217 |
| - break; |
218 |
| - } |
219 |
| - break; |
220 |
| - case FRAME_RMC: //************* GPRMC FRAME parsing |
221 |
| - switch (param) { |
222 |
| - case 1: |
223 |
| - gps_Msg.time = grab_fields(string, 0); |
224 |
| - break; |
225 |
| - case 2: |
226 |
| - if (string[0] == 'A') { |
227 |
| - gps_Msg.fix = 1; |
228 |
| - } |
229 |
| - else { |
230 |
| - gps_Msg.fix = 0; |
231 |
| - } |
232 |
| - break; |
233 |
| - case 7: |
234 |
| - gps_Msg.speed = ((grab_fields(string, 1) * 5144L) / 1000L); // speed in cm/s added by Mis |
235 |
| - break; |
236 |
| - case 8: |
237 |
| - gps_Msg.groundCourse = (grab_fields(string, 1)); // ground course deg * 10 |
238 |
| - break; |
239 |
| - case 9: |
240 |
| - gps_Msg.date = grab_fields(string, 0); |
241 |
| - break; |
242 |
| - } |
243 |
| - break; |
244 |
| - |
| 121 | + if (gpsNewFrameUBX(c, true)) { |
| 122 | + gpsProtocol = GPS_PROTOCOL_UBX; |
| 123 | + state = 0; |
| 124 | + return; |
245 | 125 | }
|
246 | 126 |
|
247 |
| - param++; |
248 |
| - offset = 0; |
249 |
| - if (c == '*') |
250 |
| - checksum_param = 1; |
251 |
| - else |
252 |
| - parity ^= c; |
253 |
| - break; |
254 |
| - case '\r': |
255 |
| - case '\n': |
256 |
| - if (checksum_param) { //parity checksum |
257 |
| - uint8_t checksum = 16 * ((string[0] >= 'A') ? string[0] - 'A' + 10 : string[0] - '0') + |
258 |
| - ((string[1] >= 'A') ? string[1] - 'A' + 10 : string[1] - '0'); |
259 |
| - if (checksum == parity) { |
260 |
| - gpsData.packetCount++; |
261 |
| - switch (gps_frame) { |
262 |
| - case FRAME_GGA: |
263 |
| - frameOK = 1; |
264 |
| - gpsData.fix = gps_Msg.fix; |
265 |
| - gpsData.numSat = gps_Msg.numSat; |
266 |
| - gpsData.hdop = gps_Msg.hdop; |
267 |
| - if (gps_Msg.fix) { |
268 |
| - __disable_irq(); // do the atomic update of lat/lon |
269 |
| - gpsData.latitude = gps_Msg.latitude; |
270 |
| - gpsData.longitude = gps_Msg.longitude; |
271 |
| - gpsData.altitude = gps_Msg.altitude; |
272 |
| - __enable_irq(); |
273 |
| - } |
274 |
| - break; |
275 |
| - case FRAME_RMC: |
276 |
| - gpsData.speed = gps_Msg.speed; |
277 |
| - gpsData.groundCourse = gps_Msg.groundCourse; |
278 |
| -#if defined(RTCLOCK) |
279 |
| - // set RTC clock if needed |
280 |
| - if (g_eeGeneral.adjustRTC && gps_Msg.fix) { |
281 |
| - div_t qr = div(gps_Msg.date, 100); |
282 |
| - uint8_t year = qr.rem; |
283 |
| - qr = div(qr.quot, 100); |
284 |
| - uint8_t mon = qr.rem; |
285 |
| - uint8_t day = qr.quot; |
286 |
| - qr = div(gps_Msg.time, 100); |
287 |
| - uint8_t sec = qr.rem; |
288 |
| - qr = div(qr.quot, 100); |
289 |
| - uint8_t min = qr.rem; |
290 |
| - uint8_t hour = qr.quot; |
291 |
| - rtcAdjust(year+2000, mon, day, hour, min, sec); |
292 |
| - } |
293 |
| -#endif |
294 |
| - } // end switch |
295 |
| - } |
296 |
| - else { |
297 |
| - gpsData.errorCount++; |
298 |
| - } |
| 127 | + uint32_t new_time = get_tmr10ms(); |
| 128 | + if (new_time - time > 20) { |
| 129 | + // No message received |
| 130 | + changeBaudrate(); |
| 131 | + time = new_time; |
299 | 132 | }
|
300 |
| - checksum_param = 0; |
301 |
| - break; |
302 |
| - default: |
303 |
| - if (offset < 15) |
304 |
| - string[offset++] = c; |
305 |
| - if (!checksum_param) |
306 |
| - parity ^= c; |
307 | 133 | }
|
308 |
| - return frameOK; |
309 | 134 | }
|
310 | 135 |
|
311 |
| -bool gpsNewFrame(uint8_t c) |
| 136 | +static void detectDisconnected(bool has_frame) |
312 | 137 | {
|
313 |
| - return gpsNewFrameNMEA(c); |
| 138 | + static tmr10ms_t time = 0; |
| 139 | + |
| 140 | + if (has_frame) { |
| 141 | + time = get_tmr10ms(); |
| 142 | + } else if (time > 0 && get_tmr10ms() - time > 500) { |
| 143 | + gpsProtocol = GPS_PROTOCOL_AUTO; |
| 144 | + time = get_tmr10ms(); |
| 145 | + } |
314 | 146 | }
|
315 | 147 |
|
| 148 | + |
316 | 149 | void gpsNewData(uint8_t c)
|
317 | 150 | {
|
318 |
| - if (!gpsNewFrame(c)) { |
319 |
| - return; |
| 151 | + switch (gpsProtocol) { |
| 152 | + case GPS_PROTOCOL_NMEA: |
| 153 | + detectDisconnected(gpsNewFrameNMEA(c)); |
| 154 | + break; |
| 155 | + case GPS_PROTOCOL_UBX: |
| 156 | + detectDisconnected(gpsNewFrameUBX(c, false)); |
| 157 | + break; |
| 158 | + case GPS_PROTOCOL_AUTO: |
| 159 | + autodetectProtocol(c); |
| 160 | + break; |
320 | 161 | }
|
321 | 162 | }
|
322 | 163 |
|
323 |
| -static const etx_serial_driver_t* gpsSerialDrv = nullptr; |
324 |
| -static void* gpsSerialCtx = nullptr; |
325 | 164 |
|
326 | 165 | #if defined(DEBUG)
|
327 | 166 | uint8_t gpsTraceEnabled = false;
|
328 | 167 | #endif
|
329 | 168 |
|
330 |
| -void gpsSetSerialDriver(void* ctx, const etx_serial_driver_t* drv) |
| 169 | +void gpsSetSerialDriver(void* ctx, const etx_serial_driver_t* drv, int protocol) |
331 | 170 | {
|
| 171 | + gpsProtocol = protocol; |
332 | 172 | gpsSerialCtx = ctx;
|
333 | 173 | gpsSerialDrv = drv;
|
334 | 174 | }
|
335 | 175 |
|
336 | 176 | void gpsWakeup()
|
337 | 177 | {
|
338 | 178 | if (!gpsSerialDrv) return;
|
339 |
| - |
| 179 | + |
340 | 180 | auto _getByte = gpsSerialDrv->getByte;
|
341 | 181 | if (!_getByte) return;
|
342 | 182 |
|
| 183 | + static tmr10ms_t time = get_tmr10ms(); |
343 | 184 | uint8_t byte;
|
344 | 185 | while (_getByte(gpsSerialCtx, &byte)) {
|
345 | 186 | #if defined(DEBUG)
|
346 | 187 | if (gpsTraceEnabled) {
|
347 | 188 | dbgSerialPutc(byte);
|
348 | 189 | }
|
349 |
| -#endif |
| 190 | +#endif |
350 | 191 | gpsNewData(byte);
|
| 192 | + time = get_tmr10ms(); |
351 | 193 | }
|
352 |
| -} |
353 | 194 |
|
354 |
| -char hex(uint8_t b) { |
355 |
| - return b > 9 ? b + 'A' - 10 : b + '0'; |
| 195 | + if (get_tmr10ms() - time > 20) { |
| 196 | + changeBaudrate(); |
| 197 | + time = get_tmr10ms(); |
| 198 | + } |
356 | 199 | }
|
357 | 200 |
|
358 | 201 | void gpsSendFrame(const char * frame)
|
359 | 202 | {
|
360 |
| - if (!gpsSerialDrv) return; |
361 |
| - |
362 |
| - auto _sendByte = gpsSerialDrv->sendByte; |
363 |
| - if (!_sendByte) return; |
364 |
| - |
365 |
| - // send given frame, add checksum and CRLF |
366 |
| - uint8_t parity = 0; |
367 |
| - TRACE_NOCRLF("gps> %s", frame); |
368 |
| - |
369 |
| - while (*frame) { |
370 |
| - if (*frame != '$') parity ^= *frame; |
371 |
| - _sendByte(gpsSerialCtx, *frame); |
372 |
| - ++frame; |
| 203 | + switch (gpsProtocol) { |
| 204 | + case GPS_PROTOCOL_NMEA: |
| 205 | + gpsSendFrameNMEA(frame); |
| 206 | + break; |
373 | 207 | }
|
374 |
| - |
375 |
| - _sendByte(gpsSerialCtx, '*'); |
376 |
| - _sendByte(gpsSerialCtx, hex(parity >> 4)); |
377 |
| - _sendByte(gpsSerialCtx, hex(parity & 0x0F)); |
378 |
| - _sendByte(gpsSerialCtx, '\r'); |
379 |
| - _sendByte(gpsSerialCtx, '\n'); |
380 |
| - |
381 |
| - TRACE("*%02x", parity); |
382 | 208 | }
|
0 commit comments