/src/gpsd/gpsd-3.25.1~dev/drivers/driver_skytraq.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * This is the gpsd driver for Skytraq GPSes operating in binary mode. |
3 | | * |
4 | | * SkyTraq is Big Endian |
5 | | * |
6 | | * This file is Copyright 2016 by the GPSD project |
7 | | * SPDX-License-Identifier: BSD-2-clause |
8 | | */ |
9 | | |
10 | | #include "../include/gpsd_config.h" // must be before all includes |
11 | | |
12 | | #include <ctype.h> |
13 | | #include <math.h> |
14 | | #include <stdbool.h> |
15 | | #include <stdio.h> |
16 | | #include <stdlib.h> |
17 | | #include <string.h> // for strlcpy() |
18 | | #include <strings.h> |
19 | | #include <unistd.h> |
20 | | |
21 | | #include "../include/gpsd.h" |
22 | | #include "../include/bits.h" |
23 | | #include "../include/strfuncs.h" |
24 | | #if defined(SKYTRAQ_ENABLE) |
25 | | #include "../include/timespec.h" |
26 | | |
27 | | #define HI(n) ((n) >> 8) |
28 | | #define LO(n) ((n) & 0xff) |
29 | | |
30 | | /* |
31 | | * No ACK/NAK? Just retry after 6 seconds |
32 | | */ |
33 | | #define SKY_RETRY_TIME 6 |
34 | | // Phoenix has 230 channels |
35 | 0 | #define SKY_CHANNELS 230 // max channels allowed in format |
36 | | |
37 | | #ifdef __UNUSED |
38 | | // Poll Software Version MID 2 |
39 | | static unsigned char versionprobe[] = { |
40 | | 0xa0, 0xa1, 0x00, 0x02, |
41 | | 0x02, // MID 2 |
42 | | 0x01, // System |
43 | | 0x00, 0x0d, 0x0a |
44 | | }; |
45 | | #endif // __UNUSED |
46 | | |
47 | | /* place checksum into msg, write to device |
48 | | * Return: number of bytes written |
49 | | * negative on error |
50 | | */ |
51 | | static ssize_t sky_write(struct gps_device_t *session, char *msg, |
52 | | size_t data_len) |
53 | 0 | { |
54 | 0 | uint8_t chk; |
55 | 0 | uint16_t len; |
56 | 0 | ssize_t i; |
57 | 0 | bool ok; |
58 | 0 | unsigned type = (unsigned)msg[4]; |
59 | 0 | uint8_t buf[BUFSIZ]; |
60 | 0 | uint8_t outbuf[BUFSIZ]; |
61 | | |
62 | | // do not write if -b (readonly) option set |
63 | | // "passive" handled earlier |
64 | 0 | if (session->context->readonly) { |
65 | 0 | return data_len; |
66 | 0 | } |
67 | | |
68 | 0 | if (sizeof(buf) <= data_len) { |
69 | | // uh, oh; |
70 | 0 | return -1; |
71 | 0 | } |
72 | | // make a copy, so we can edit it |
73 | 0 | memcpy(buf, msg, data_len); |
74 | | |
75 | | // max length is undocumented, largest I could find is 261 |
76 | 0 | len = (buf[2] << 8) | buf[3]; |
77 | | // limit to 512 to pacify coverity |
78 | 0 | if (512 < len) { |
79 | 0 | len = 512; |
80 | 0 | } |
81 | 0 | if ((size_t)(len + 7) != data_len) { |
82 | | // uh, oh; |
83 | 0 | GPSD_LOG(LOG_ERROR, &session->context->errout, |
84 | 0 | "Skytraq: Length error: len %u data_len %zu buf %s\n", |
85 | 0 | len, data_len, |
86 | 0 | gps_hexdump((char *)outbuf, sizeof(outbuf), |
87 | 0 | buf, data_len)); |
88 | 0 | return -2; |
89 | 0 | } |
90 | | |
91 | | // calculate Checksum |
92 | 0 | chk = 0; |
93 | | // coverity_submit[tainted_data] |
94 | 0 | for (i = 0; i < len; i++) { |
95 | 0 | chk ^= buf[4 + i]; |
96 | 0 | } |
97 | | |
98 | | // enter checksum after payload |
99 | 0 | buf[len + 4] = chk; |
100 | 0 | len += 7; |
101 | |
|
102 | 0 | GPSD_LOG(LOG_IO, &session->context->errout, |
103 | 0 | "Skytraq: Writing control MID %02x: %s\n", type, |
104 | 0 | gps_hexdump((char *)outbuf, sizeof(outbuf), buf, len)); |
105 | 0 | ok = gpsd_write(session, (const char *)buf, len) == len; |
106 | |
|
107 | 0 | return ok; |
108 | 0 | } |
109 | | |
110 | | /* sky_mode() - NMEA/Binary mode changer |
111 | | * return: void |
112 | | * |
113 | | * Cherry-picked from: |
114 | | * 0004-Update-Skytraq-driver-to-support-Venus8-modules-chan.patch |
115 | | * by Kai Harrekilde-Petersen in: |
116 | | * https://lists.gnu.org/archive/html/gpsd-dev/2018-03/msg00025.html |
117 | | */ |
118 | | static void sky_mode(struct gps_device_t *session, int mode) |
119 | 0 | { |
120 | 0 | char msg[] = { |
121 | 0 | 0xA0, 0xA1, // start-of-sentence sequence |
122 | 0 | 0x00, 0x03, |
123 | 0 | 0x09, // SKY_CONFIG_MSG_TYPE |
124 | 0 | 0x00, 0x00, 0x00, |
125 | 0 | 0x0D, 0x0A // end-of-sentence sequence |
126 | 0 | }; |
127 | 0 | if (MODE_BINARY == mode) { |
128 | 0 | msg[5] = 0x02; |
129 | 0 | } else { // else to MODE_NMEA |
130 | 0 | msg[5] = 0x01; |
131 | 0 | } |
132 | |
|
133 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
134 | 0 | "Skytraq: setting MODE %s\n", |
135 | 0 | (MODE_BINARY == mode) ? "Binary" : "NMEA"); |
136 | 0 | (void)sky_write(session, msg, 10); |
137 | 0 | } |
138 | | |
139 | | /* |
140 | | * Convert PRN to gnssid and svid |
141 | | */ |
142 | | static void PRN2_gnssId_svId(short PRN, uint8_t *gnssId, uint8_t *svId) |
143 | 0 | { |
144 | | // fit into gnssid:svid |
145 | 0 | if (0 == PRN) { |
146 | | // skip 0 PRN |
147 | 0 | *gnssId = 0; |
148 | 0 | *svId = 0; |
149 | 0 | } else if ((1 <= PRN) && |
150 | 0 | (32 >= PRN)) { |
151 | | // GPS |
152 | 0 | *gnssId = 0; |
153 | 0 | *svId = PRN; |
154 | 0 | } else if ((65 <= PRN) && |
155 | 0 | (96 >= PRN)) { |
156 | | // GLONASS |
157 | 0 | *gnssId = 6; |
158 | 0 | *svId = PRN - 64; |
159 | 0 | } else if ((120 <= PRN) && |
160 | 0 | (158 >= PRN)) { |
161 | | // SBAS |
162 | 0 | *gnssId = 1; |
163 | 0 | *svId = PRN; |
164 | 0 | } else if ((201 <= PRN) && |
165 | 0 | (239 >= PRN)) { |
166 | | // BeiDou |
167 | 0 | *gnssId = 3; |
168 | 0 | *svId = PRN - 200; |
169 | 0 | } else if ((240 <= PRN) && |
170 | 0 | (254 >= PRN)) { |
171 | | // IRNSS |
172 | 0 | *gnssId = 20; |
173 | 0 | *svId = PRN - 240; |
174 | 0 | } else { |
175 | | // huh? |
176 | 0 | *gnssId = 0; |
177 | 0 | *svId = 0; |
178 | 0 | } |
179 | 0 | return; |
180 | 0 | } |
181 | | |
182 | | /* |
183 | | decode MID 0x62 -- super packet |
184 | | * |
185 | | * Present in Phoenix |
186 | | */ |
187 | | static gps_mask_t sky_msg_62(struct gps_device_t *session, |
188 | | unsigned char *buf, size_t len) |
189 | 0 | { |
190 | 0 | unsigned sid; |
191 | 0 | unsigned u[23]; |
192 | 0 | int i; |
193 | |
|
194 | 0 | if (3 > len) { |
195 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
196 | 0 | "Skytraq 0x62: bad len %zu\n", len); |
197 | 0 | return 0; |
198 | 0 | } |
199 | | |
200 | 0 | sid = getub(buf, 1); |
201 | 0 | switch (sid) { |
202 | 0 | case 0x80: |
203 | | // SBAS status |
204 | 0 | for (i = 0; i < 6; i++) { |
205 | 0 | u[i] = getub(buf, i + 2); |
206 | 0 | } |
207 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
208 | 0 | "Skytraq 0x62/80: enable %u ranging %u URA mask %u " |
209 | 0 | "correction %u chans %u subsystems %u \n", |
210 | 0 | u[0], u[1], u[2], u[3], u[4], u[5]); |
211 | 0 | break; |
212 | 0 | case 0x81: |
213 | | // QXSS status |
214 | 0 | u[0] = getub(buf, 2); |
215 | 0 | u[1] = getub(buf, 3); |
216 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
217 | 0 | "Skytraq 0x62/81: enable %u chans %u\n", |
218 | 0 | u[0], u[1]); |
219 | 0 | break; |
220 | 0 | case 0x82: |
221 | | // SBAS advanced status |
222 | 0 | for (i = 0; i < 22; i++) { |
223 | 0 | u[i] = getub(buf, i + 2); |
224 | 0 | } |
225 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
226 | 0 | "Skytraq 0x62/82: enable %u ranging %u URA %u corr %u " |
227 | 0 | "chans %u mask x%02x WAAS %u %u %u %u " |
228 | 0 | "EGNOS %u %u %u %u MSAS %u %u %u %u " |
229 | 0 | "GAGAN %u %u %u %u\n", |
230 | 0 | u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], |
231 | 0 | u[9], u[10], u[11], u[12], u[13], u[14], u[15], u[16], u[17], |
232 | 0 | u[18], u[19], u[20], u[21]); |
233 | 0 | break; |
234 | 0 | default: |
235 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
236 | 0 | "Skytraq 0x62: SID x%02x len %zu\n", sid, len); |
237 | 0 | } |
238 | 0 | return 0; |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | * decode MID 0x63 -- super packet |
243 | | * |
244 | | * Present in Phoenix |
245 | | */ |
246 | | static gps_mask_t sky_msg_63(struct gps_device_t *session, |
247 | | unsigned char *buf, size_t len) |
248 | 0 | { |
249 | 0 | unsigned sid; |
250 | |
|
251 | 0 | if (3 > len) { |
252 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
253 | 0 | "Skytraq 0x63: bad len %zu\n", len); |
254 | 0 | return 0; |
255 | 0 | } |
256 | | |
257 | 0 | sid = getub(buf, 1); |
258 | | |
259 | | // FIXME: decode them! |
260 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
261 | 0 | "Skytraq 0x63: SID %u\n", sid); |
262 | 0 | return 0; |
263 | 0 | } |
264 | | |
265 | | /* |
266 | | * decode MID 0x64 -- super packet |
267 | | * |
268 | | * Present in Phoenix |
269 | | */ |
270 | | static gps_mask_t sky_msg_64(struct gps_device_t *session, |
271 | | unsigned char *buf, size_t len) |
272 | 0 | { |
273 | 0 | unsigned sid; |
274 | 0 | unsigned u[13]; |
275 | 0 | int i; |
276 | 0 | int s[3]; |
277 | |
|
278 | 0 | if (3 > len) { |
279 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
280 | 0 | "Skytraq 0x64: bad len %zu\n", len); |
281 | 0 | return 0; |
282 | 0 | } |
283 | | |
284 | 0 | sid = getub(buf, 1); |
285 | 0 | switch (sid) { |
286 | 0 | case 0x80: |
287 | | // GNSS Boot status |
288 | 0 | u[0] = getub(buf, 2); |
289 | 0 | u[1] = getub(buf, 3); |
290 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
291 | 0 | "Skytraq 0x64/80: enable %u type %u\n", |
292 | 0 | u[0], u[1]); |
293 | 0 | break; |
294 | 0 | case 0x81: |
295 | | // Extended NMEA Message Interval |
296 | 0 | for (i = 0; i < 12; i++) { |
297 | 0 | u[i] = getub(buf, i + 2); |
298 | 0 | } |
299 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
300 | 0 | "Skytraq 0x64/81: GGA %u GSA %u GSV %u GLL %u RMC %u " |
301 | 0 | "VTG %u ZDA %u GNS %u GBS %u GRS %u DTM %u GST %u\n", |
302 | 0 | u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], |
303 | 0 | u[9], u[10], u[11]); |
304 | 0 | break; |
305 | 0 | case 0x83: |
306 | | // Interference Detection Status |
307 | 0 | u[0] = getub(buf, 2); |
308 | 0 | u[1] = getub(buf, 3); |
309 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
310 | 0 | "Skytraq 0x64/83: enable %u status %u\n", |
311 | 0 | u[0], u[1]); |
312 | 0 | break; |
313 | 0 | case 0x85: |
314 | | // GPS PARAMETER SEARCH ENGINE NUMBER |
315 | 0 | u[0] = getub(buf, 2); |
316 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
317 | 0 | "Skytraq 0x64/85: search engine number %u\n", u[0]); |
318 | 0 | break; |
319 | 0 | case 0x88: |
320 | | // Position/Fix navigation mask |
321 | 0 | u[0] = getub(buf, 2); |
322 | 0 | u[1] = getub(buf, 3); |
323 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
324 | 0 | "Skytraq 0x64/88: 1st %u subsequent %u\n", |
325 | 0 | u[0], u[1]); |
326 | 0 | break; |
327 | 0 | case 0x8a: |
328 | | // GPS UTC Reference time |
329 | 0 | u[0] = getub(buf, 2); |
330 | 0 | u[1] = getbeu16(buf, 3); |
331 | 0 | u[2] = getub(buf, 5); |
332 | 0 | u[3] = getub(buf, 6); |
333 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
334 | 0 | "Skytraq 0x64/8a: enable %u year %u month %u day %u\n", |
335 | 0 | u[0], u[1], u[2], u[3]); |
336 | 0 | break; |
337 | 0 | case 0x8b: |
338 | | // GNSS Nav mode |
339 | 0 | u[0] = getub(buf, 2); |
340 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
341 | 0 | "Skytraq 0x64/8b: mode %u\n", u[0]); |
342 | 0 | break; |
343 | 0 | case 0x8c: |
344 | | // GNSS Constellation type for nav solution |
345 | 0 | u[0] = getbeu16(buf, 2); |
346 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
347 | 0 | "Skytraq 0x64/8c: Nav Type x%02x\n", u[0]); |
348 | 0 | break; |
349 | 0 | case 0x8e: |
350 | | // GPS time |
351 | 0 | u[0] = getbeu32(buf, 2); // TOW ms |
352 | 0 | u[1] = getbeu32(buf, 6); // TOW ns |
353 | 0 | u[2] = getbeu16(buf, 10); // GPS week |
354 | 0 | s[0] = getsb(buf, 12); // default leap s |
355 | 0 | s[1] = getsb(buf, 13); // current leap s |
356 | 0 | u[3] = getub(buf, 14); // valid |
357 | | // FIXME: save GPS week and leap s |
358 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
359 | 0 | "Skytraq 0x64/8a: TOW %u %u week %u leap %d %d valid x%x\n", |
360 | 0 | u[0], u[1], u[2], s[0], s[1], u[3]); |
361 | 0 | break; |
362 | 0 | case 0x92: |
363 | | // GLONASS Time corrections |
364 | 0 | s[0] = getbes32(buf, 2); // tau c |
365 | 0 | s[1] = getbes32(buf, 6); // tau gps |
366 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
367 | 0 | "Skytraq 0x64/92: tau c %d tau GPS %d\n", |
368 | 0 | s[0], s[1]); |
369 | 0 | break; |
370 | 0 | case 0xfe: |
371 | | // Version extension string |
372 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
373 | 0 | "Skytraq 0x64/fe: >%.32s<\n", &buf[2]); |
374 | 0 | break; |
375 | 0 | default: |
376 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
377 | 0 | "Skytraq 0x64: SID x%02x len %zu\n", sid, len); |
378 | 0 | } |
379 | 0 | return 0; |
380 | 0 | } |
381 | | |
382 | | /* |
383 | | * decode MID 0x65 -- super packet |
384 | | * |
385 | | * Present in Phoenix |
386 | | */ |
387 | | static gps_mask_t sky_msg_65(struct gps_device_t *session, |
388 | | unsigned char *buf, size_t len) |
389 | 0 | { |
390 | 0 | unsigned sid; |
391 | 0 | unsigned u[13]; |
392 | |
|
393 | 0 | if (3 > len) { |
394 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
395 | 0 | "Skytraq 0x65: bad len %zu\n", len); |
396 | 0 | return 0; |
397 | 0 | } |
398 | | |
399 | 0 | sid = getub(buf, 1); |
400 | 0 | switch (sid) { |
401 | 0 | case 0x80: |
402 | | // 1PPS Pulse width |
403 | 0 | u[0] = getbeu32(buf, 2); // pulse width miicro seconds |
404 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
405 | 0 | "Skytraq 0x65/80: width %u\n", u[0]); |
406 | 0 | break; |
407 | 0 | case 0x81: |
408 | | // PPS2 frequency |
409 | 0 | u[0] = getbeu32(buf, 2); // freq of PPS2 Hz |
410 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
411 | 0 | "Skytraq 0x65/81: PPS2 Hz %u\n", u[0]); |
412 | 0 | break; |
413 | 0 | default: |
414 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
415 | 0 | "Skytraq 0x65: SID x%02x len %zu\n", sid, len); |
416 | 0 | } |
417 | 0 | return 0; |
418 | 0 | } |
419 | | |
420 | | /* |
421 | | * decode MID 0x6A -- super packet |
422 | | * |
423 | | * Present in Phoenix |
424 | | */ |
425 | | static gps_mask_t sky_msg_6A(struct gps_device_t *session, |
426 | | unsigned char *buf, size_t len) |
427 | 0 | { |
428 | 0 | unsigned sid; |
429 | 0 | unsigned u[13]; |
430 | 0 | double d[5]; |
431 | |
|
432 | 0 | if (3 > len) { |
433 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
434 | 0 | "Skytraq 0x6A: bad len %zu\n", len); |
435 | 0 | return 0; |
436 | 0 | } |
437 | | |
438 | 0 | sid = getub(buf, 1); |
439 | 0 | switch (sid) { |
440 | 0 | case 0x83: |
441 | | // RTK mode and operational functioN |
442 | 0 | u[0] = getub(buf, 2); // RTK mode |
443 | 0 | u[1] = getub(buf, 3); // RTK function |
444 | 0 | u[2] = getbeu32(buf, 4); // saved survey length |
445 | 0 | u[3] = getbeu32(buf, 8); // standard deviation |
446 | 0 | d[0] = getled64((char *)buf, 12); // latitude |
447 | 0 | d[1] = getled64((char *)buf, 20); // longitude |
448 | 0 | d[3] = getlef32((char *)buf, 28); // altitude (HAE or MSL?) |
449 | 0 | u[4] = getub(buf, 32); // runtime function |
450 | 0 | u[5] = getbeu32(buf, 33); // run-time survey length |
451 | 0 | d[4] = getlef32((char *)buf, 37); // baseline length constant |
452 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
453 | 0 | "Skytraq 0x6A/83: mode %u func %u len %u sdev %u lat %.8f " |
454 | 0 | "lon %.8f alt %.4f func %u len %u len %.4f\n", |
455 | 0 | u[0], u[1], u[2], u[3], d[0], d[1], d[3], u[4], u[5], d[4]); |
456 | 0 | break; |
457 | 0 | case 0x85: |
458 | | // RTK slave base serial port baud ratE |
459 | 0 | u[0] = getub(buf, 2); // rate code |
460 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
461 | 0 | "Skytraq 0x6A/85: rate %u\n", u[0]); |
462 | 0 | break; |
463 | 0 | case 0x88: |
464 | | // RTK kinematic base serial port baud ratE |
465 | 0 | u[0] = getub(buf, 2); // rate code |
466 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
467 | 0 | "Skytraq 0x6A/88: rate %u\n", u[0]); |
468 | 0 | break; |
469 | 0 | default: |
470 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
471 | 0 | "Skytraq 0x6A: SID x%02x len %zu\n", sid, len); |
472 | 0 | } |
473 | 0 | return 0; |
474 | 0 | } |
475 | | |
476 | | /* |
477 | | * decode MID 0x7A -- super packet |
478 | | * |
479 | | * Present in Phoenix |
480 | | */ |
481 | | static gps_mask_t sky_msg_7A(struct gps_device_t *session, |
482 | | unsigned char *buf, size_t len) |
483 | 0 | { |
484 | 0 | unsigned sid, ssid; |
485 | 0 | unsigned u[16]; |
486 | 0 | double d[2]; |
487 | 0 | int i; |
488 | |
|
489 | 0 | if (3 > len) { |
490 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
491 | 0 | "Skytraq 0x7A: bad len %zu\n", len); |
492 | 0 | return 0; |
493 | 0 | } |
494 | | |
495 | 0 | sid = getub(buf, 1); |
496 | 0 | ssid = getub(buf, 2); |
497 | 0 | switch ((sid << 8) | ssid) { |
498 | 0 | case 0x0e80: |
499 | | // Moving base software version |
500 | 0 | for (i = 0; i < 13; i++) { |
501 | 0 | u[i] = getub(buf, i + 3); |
502 | 0 | } |
503 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
504 | 0 | "Skytraq 0x7A/0E/80: type %u " |
505 | 0 | "kver %u.%u.%u over %u.%u.%u rev %02u.%02u.%02u\n", |
506 | 0 | u[0], u[2], u[3], u[4], u[6], u[7], u[8], u[10], |
507 | 0 | u[11], u[12]); |
508 | 0 | break; |
509 | 0 | case 0x0e81: |
510 | | // Moving base software CRC |
511 | 0 | u[0] = getub(buf, 3); |
512 | 0 | u[1] = getbeu16(buf, 4); |
513 | |
|
514 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
515 | 0 | "Skytraq 0x7A/0E/801: type %u crc %u\n", u[0], u[1]); |
516 | 0 | break; |
517 | 0 | case 0x0e82: |
518 | | // Moving base pos update rate |
519 | 0 | u[0] = getub(buf, 3); |
520 | |
|
521 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
522 | 0 | "Skytraq 0x7A/0E/802: rate %u\n", u[0]); |
523 | 0 | break; |
524 | 0 | case 0x0e83: |
525 | | // Moving base heading and pitch offsets |
526 | 0 | d[0] = getbeu32(buf, 3); // heading |
527 | 0 | d[1] = getbeu32(buf, 7); // pitch |
528 | |
|
529 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
530 | 0 | "Skytraq 0x7A/0E/803: heading %f pitch %f\n", d[0], d[1]); |
531 | 0 | break; |
532 | 0 | default: |
533 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
534 | 0 | "Skytraq 0x7A: SID x%02x/%02x len %zu\n", sid, ssid, len); |
535 | 0 | } |
536 | 0 | return 0; |
537 | 0 | } |
538 | | |
539 | | /* |
540 | | * decode MID 0x80, Software Version |
541 | | * |
542 | | * 10 bytes |
543 | | * |
544 | | * Present in: Venus 6 |
545 | | * Venus 8 |
546 | | * Phoenix |
547 | | */ |
548 | | static gps_mask_t sky_msg_80(struct gps_device_t *session, |
549 | | unsigned char *buf, size_t len) |
550 | 0 | { |
551 | 0 | unsigned kver_x; // kernel version |
552 | 0 | unsigned kver_y; // kernel version |
553 | 0 | unsigned kver_z; // kernel version |
554 | 0 | unsigned over_x; // ODM version |
555 | 0 | unsigned over_y; // ODM version |
556 | 0 | unsigned over_z; // ODM version |
557 | 0 | unsigned rev_yy; // revision |
558 | 0 | unsigned rev_mm; // revision |
559 | 0 | unsigned rev_dd; // revision |
560 | |
|
561 | 0 | if (14 != len) { |
562 | 0 | return 0; |
563 | 0 | } |
564 | | |
565 | 0 | kver_x = getbeu16(buf, 2); |
566 | 0 | kver_y = getub(buf, 4); |
567 | 0 | kver_z = getub(buf, 5); |
568 | 0 | over_x = getbeu16(buf, 6); |
569 | 0 | over_y = getub(buf, 8); |
570 | 0 | over_z = getub(buf, 9); |
571 | 0 | rev_yy = getbeu16(buf, 10); |
572 | 0 | rev_mm = getub(buf, 12); |
573 | 0 | rev_dd = getub(buf, 13); |
574 | |
|
575 | 0 | (void)snprintf(session->subtype, sizeof(session->subtype) - 1, |
576 | 0 | "kver %u.%u.%u over %u.%u.%u rev %02u.%02u.%02u", |
577 | 0 | kver_x, kver_y, kver_z, |
578 | 0 | over_x, over_y, over_z, |
579 | 0 | rev_yy, rev_mm, rev_dd); |
580 | |
|
581 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
582 | 0 | "Skytraq 0x80: %s\n", |
583 | 0 | session->subtype); |
584 | 0 | return 0; |
585 | 0 | } |
586 | | |
587 | | /* |
588 | | * decode MID 0x81 - Software CRC |
589 | | * |
590 | | * Present in Phoenix |
591 | | */ |
592 | | static gps_mask_t sky_msg_81(struct gps_device_t *session, |
593 | | unsigned char *buf, size_t len) |
594 | 0 | { |
595 | 0 | unsigned type, crc; |
596 | |
|
597 | 0 | if (4 != len) { |
598 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
599 | 0 | "Skytraq 0x81: bad len %zu\n", len); |
600 | 0 | return 0; |
601 | 0 | } |
602 | | |
603 | 0 | type = getub(buf, 1); |
604 | 0 | crc = getbeu16(buf, 2); |
605 | |
|
606 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
607 | 0 | "Skytraq 0x81: type %u crc %u\n", type, crc); |
608 | 0 | return 0; |
609 | 0 | } |
610 | | |
611 | | /* |
612 | | * decode MID 0x86 - Position Update Rate |
613 | | * |
614 | | * Present in Phoenix |
615 | | */ |
616 | | static gps_mask_t sky_msg_86(struct gps_device_t *session, |
617 | | unsigned char *buf, size_t len) |
618 | 0 | { |
619 | 0 | unsigned rate; |
620 | |
|
621 | 0 | if (2 != len) { |
622 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
623 | 0 | "Skytraq 0x86: bad len %zu\n", len); |
624 | 0 | return 0; |
625 | 0 | } |
626 | | |
627 | 0 | rate = getub(buf, 1); |
628 | |
|
629 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
630 | 0 | "Skytraq 0x86: rate %u\n", rate); |
631 | 0 | return 0; |
632 | 0 | } |
633 | | |
634 | | /* |
635 | | * decode MID 0x89 - Binary measurement data output status |
636 | | * |
637 | | * Present in Phoenix |
638 | | */ |
639 | | static gps_mask_t sky_msg_89(struct gps_device_t *session, |
640 | | unsigned char *buf, size_t len) |
641 | 0 | { |
642 | 0 | unsigned i; |
643 | 0 | uint8_t u[7]; |
644 | |
|
645 | 0 | if (8 != len) { |
646 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
647 | 0 | "Skytraq 0x89: bad len %zu\n", len); |
648 | 0 | return 0; |
649 | 0 | } |
650 | | |
651 | 0 | for (i = 0; i < 7; i++) { |
652 | 0 | u[i] = getub(buf, i + 1); |
653 | 0 | } |
654 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
655 | 0 | "Skytraq 0x89: rate %u Meas %u raw %u CH_status %u " |
656 | 0 | "RCV_statas %u subf %u eraw %u\n", |
657 | 0 | u[0], u[1], u[2], u[3], u[4], u[5], u[6]); |
658 | |
|
659 | 0 | return 0; |
660 | 0 | } |
661 | | |
662 | | /* |
663 | | * decode MID 0x8A - Binary rtcm data output status |
664 | | * |
665 | | * Present in Phoenix |
666 | | */ |
667 | | static gps_mask_t sky_msg_8A(struct gps_device_t *session, |
668 | | unsigned char *buf, size_t len) |
669 | 0 | { |
670 | 0 | unsigned i; |
671 | 0 | uint8_t u[15]; |
672 | |
|
673 | 0 | if (16 != len) { |
674 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
675 | 0 | "Skytraq 0x8A: bad len %zu\n", len); |
676 | 0 | return 0; |
677 | 0 | } |
678 | | |
679 | 0 | for (i = 0; i < 15; i++) { |
680 | 0 | u[i] = getub(buf, i + 1); |
681 | 0 | } |
682 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
683 | 0 | "Skytraq 0x8A: enable %u MSM %u 1005 %u 107x %u 108x %u " |
684 | 0 | "109x %u 110x %u 111x %u 112x %u 1019 %u 1020 %u " |
685 | 0 | "1042 %u 1046 %u type %u version %u\n", |
686 | 0 | u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], |
687 | 0 | u[8], u[9], u[10], u[11], u[12], u[13], u[14]); |
688 | |
|
689 | 0 | return 0; |
690 | 0 | } |
691 | | |
692 | | /* |
693 | | * decode MID 0x8B - Base position |
694 | | * |
695 | | * Present in Phoenix |
696 | | */ |
697 | | static gps_mask_t sky_msg_8B(struct gps_device_t *session, |
698 | | unsigned char *buf, size_t len) |
699 | 0 | { |
700 | 0 | uint8_t u[5]; |
701 | 0 | double d[3]; |
702 | |
|
703 | 0 | if (35 != len) { |
704 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
705 | 0 | "Skytraq 0x8B: bad len %zu\n", len); |
706 | 0 | return 0; |
707 | 0 | } |
708 | | |
709 | 0 | u[0] = getub(buf, 1); |
710 | 0 | u[1] = getbeu32(buf, 2); |
711 | 0 | u[2] = getbeu32(buf, 6); |
712 | 0 | d[0] = getbed64((const char *)buf, 10); |
713 | 0 | d[1] = getbed64((const char *)buf, 18); |
714 | 0 | d[2] = getbef32((const char *)buf, 26); |
715 | 0 | u[3] = getub(buf, 30); |
716 | 0 | u[4] = getbeu32(buf, 31); |
717 | |
|
718 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
719 | 0 | "Skytraq 0x8B: saved mode %u saved length %u stddev %u " |
720 | 0 | "lat %.9f lon %.9f HAE %0.4f run mode %u survey len %u\n", |
721 | 0 | u[0], u[1], u[2], d[0], d[1], d[2], u[3], u[4]); |
722 | |
|
723 | 0 | return 0; |
724 | 0 | } |
725 | | |
726 | | /* |
727 | | * decode MID 0x93 - NMEA Talker ID |
728 | | * |
729 | | * Present in Phoenix |
730 | | */ |
731 | | static gps_mask_t sky_msg_93(struct gps_device_t *session, |
732 | | unsigned char *buf, size_t len) |
733 | 0 | { |
734 | 0 | unsigned mode; |
735 | |
|
736 | 0 | if (2 != len) { |
737 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
738 | 0 | "Skytraq 0x93: bad len %zu\n", len); |
739 | 0 | return 0; |
740 | 0 | } |
741 | | |
742 | 0 | mode = getub(buf, 1); |
743 | |
|
744 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
745 | 0 | "Skytraq 0x93: mode %u\n", mode); |
746 | 0 | return 0; |
747 | 0 | } |
748 | | |
749 | | /* |
750 | | * Binary Navigation Data Message (0xA8) |
751 | | * Returns 59 bytes; |
752 | | * Fix Mode, SV, Week, TOW, Latitude, Longitude, ellipsoid altitude, |
753 | | * mean sea level altitude, gdop, pdop, hdop, vdop, tdop, |
754 | | * ecef.{x,y,z}, ecef_v.{x,y,z} |
755 | | * |
756 | | * The following message (0xA8) is emitted by the modules supporting |
757 | | * the Binary Messages Raw Measurements Data Extension in AN0028 |
758 | | * (https://www.skytraq.com.tw/homesite/AN0028.pdf) |
759 | | * Original implementation by Kai Harrekilde-Petersen |
760 | | * and adapted by Thatcher Chamberlin (j.thatcher.c@gmail.com) |
761 | | */ |
762 | | static gps_mask_t sky_msg_A8(struct gps_device_t *session, |
763 | | unsigned char *buf, size_t len) |
764 | 0 | { |
765 | 0 | unsigned char navmode; // Navigation fix mode (0: No fix, 1: 2D, 2: 3D, 3: 3D+DGNSS |
766 | 0 | unsigned short week; // GNSS week number |
767 | 0 | double ftow; // Time of week |
768 | 0 | timespec_t ts_tow; |
769 | 0 | char ts_buf[TIMESPEC_LEN]; |
770 | |
|
771 | 0 | int *mode = &session->newdata.mode; |
772 | 0 | int *status = &session->newdata.status; |
773 | 0 | gps_mask_t mask = 0; |
774 | |
|
775 | 0 | if (59 != len) { |
776 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, "Skytraq: " |
777 | 0 | "Navigation Data Message has incorrect length %zu\n", len); |
778 | 0 | return 0; |
779 | 0 | } |
780 | | |
781 | 0 | navmode = getub(buf, 1); |
782 | 0 | switch (navmode) { |
783 | 0 | case 1: // 2D fix |
784 | 0 | *mode = MODE_2D; |
785 | 0 | *status = STATUS_GPS; |
786 | 0 | break; |
787 | 0 | case 2: // 3D fix |
788 | 0 | *mode = MODE_3D; |
789 | 0 | *status = STATUS_GPS; |
790 | 0 | break; |
791 | 0 | case 3: // 3D DGPS fix |
792 | 0 | *mode = MODE_3D; |
793 | 0 | *status = STATUS_DGPS; |
794 | 0 | break; |
795 | | |
796 | 0 | default: // Includes SKY_MODE_NONE |
797 | 0 | *mode = MODE_NO_FIX; |
798 | 0 | *status = STATUS_UNK; |
799 | 0 | break; |
800 | 0 | } |
801 | 0 | mask |= MODE_SET | STATUS_SET; |
802 | |
|
803 | 0 | session->gpsdata.satellites_used = getub(buf, 2); |
804 | |
|
805 | 0 | mask |= ONLINE_SET; |
806 | 0 | week = getbeu16(buf, 3); |
807 | 0 | ftow = getbeu32(buf, 5) / 100.0; |
808 | 0 | DTOTS(&ts_tow, (int)ftow); |
809 | |
|
810 | 0 | session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow); |
811 | 0 | mask |= TIME_SET; |
812 | |
|
813 | 0 | if (MODE_2D == *mode || MODE_3D == *mode) { |
814 | 0 | session->newdata.latitude = getbes32(buf, 9) / 1e7; |
815 | 0 | session->newdata.longitude = getbes32(buf, 13) / 1e7; |
816 | 0 | mask |= LATLON_SET | NTPTIME_IS; |
817 | |
|
818 | 0 | if (MODE_3D == *mode) { |
819 | 0 | session->newdata.altHAE = getbes32(buf, 17) / 100.0; |
820 | 0 | session->newdata.altMSL = getbes32(buf, 21) / 100.0; |
821 | |
|
822 | 0 | session->newdata.ecef.x = getbes32(buf, 35) / 100.0; |
823 | 0 | session->newdata.ecef.y = getbes32(buf, 39) / 100.0; |
824 | 0 | session->newdata.ecef.z = getbes32(buf, 43) / 100.0; |
825 | 0 | session->newdata.ecef.vx = getbes32(buf, 47) / 100.0; |
826 | 0 | session->newdata.ecef.vy = getbes32(buf, 51) / 100.0; |
827 | 0 | session->newdata.ecef.vz = getbes32(buf, 55) / 100.0; |
828 | 0 | mask |= ECEF_SET | VECEF_SET | ALTITUDE_SET; |
829 | 0 | } |
830 | 0 | } |
831 | |
|
832 | 0 | session->gpsdata.dop.gdop = getbeu16(buf, 25) / 100.0; |
833 | 0 | session->gpsdata.dop.pdop = getbeu16(buf, 27) / 100.0; |
834 | 0 | session->gpsdata.dop.hdop = getbeu16(buf, 29) / 100.0; |
835 | 0 | session->gpsdata.dop.vdop = getbeu16(buf, 31) / 100.0; |
836 | 0 | session->gpsdata.dop.tdop = getbeu16(buf, 33) / 100.0; |
837 | 0 | mask |= DOP_SET | CLEAR_IS | REPORT_IS; |
838 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
839 | 0 | "Skytraq: NAVDATA time=%s, lat=%.7f lon=%.7f altHAE=%.2f altMSL=%.2f mode=%d status=%d " |
840 | 0 | "gdop: %.2f, hdop: %.2f, pdop: %.2f, tdop: %.2f, vdop: %.2f\n", |
841 | 0 | timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)), |
842 | 0 | session->newdata.latitude, |
843 | 0 | session->newdata.longitude, |
844 | 0 | session->newdata.altHAE, |
845 | 0 | session->newdata.altMSL, |
846 | 0 | session->newdata.mode, |
847 | 0 | session->newdata.status, |
848 | 0 | session->gpsdata.dop.gdop, |
849 | 0 | session->gpsdata.dop.hdop, |
850 | 0 | session->gpsdata.dop.pdop, |
851 | 0 | session->gpsdata.dop.tdop, |
852 | 0 | session->gpsdata.dop.vdop); |
853 | 0 | return mask; |
854 | 0 | } |
855 | | |
856 | | /* |
857 | | * decode MID 0xAE - GNSS Datum |
858 | | * |
859 | | * Present in Phoenix |
860 | | */ |
861 | | static gps_mask_t sky_msg_AE(struct gps_device_t *session, |
862 | | unsigned char *buf, size_t len) |
863 | 0 | { |
864 | 0 | unsigned datum; |
865 | |
|
866 | 0 | if (3 != len) { |
867 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
868 | 0 | "Skytraq 0xAE: bad len %zu\n", len); |
869 | 0 | return 0; |
870 | 0 | } |
871 | | |
872 | 0 | datum = getbeu16(buf, 1); |
873 | |
|
874 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
875 | 0 | "Skytraq 0xAE: datum %u\n", datum); |
876 | 0 | return 0; |
877 | 0 | } |
878 | | |
879 | | /* |
880 | | * decode MID 0xaf - DOP mask |
881 | | * |
882 | | * Present in Phoenix |
883 | | */ |
884 | | static gps_mask_t sky_msg_AF(struct gps_device_t *session, |
885 | | unsigned char *buf, size_t len) |
886 | 0 | { |
887 | 0 | unsigned mode, pdop, hdop, gdop; |
888 | |
|
889 | 0 | if (8 != len) { |
890 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
891 | 0 | "Skytraq 0xAF: bad len %zu\n", len); |
892 | 0 | return 0; |
893 | 0 | } |
894 | | |
895 | 0 | mode = getub(buf, 1); |
896 | 0 | pdop = getbeu16(buf, 2); |
897 | 0 | hdop = getbeu16(buf, 4); |
898 | 0 | gdop = getbeu16(buf, 6); |
899 | |
|
900 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
901 | 0 | "Skytraq 0xAF: Masks: mode %u pdop %u hdop %u gdop %u\n", |
902 | 0 | mode, pdop, hdop, gdop); |
903 | 0 | return 0; |
904 | 0 | } |
905 | | |
906 | | /* |
907 | | * decode MID 0xb0 Elevation and DOP mask |
908 | | * |
909 | | * Present in Phoenix |
910 | | */ |
911 | | static gps_mask_t sky_msg_B0(struct gps_device_t *session, |
912 | | unsigned char *buf, size_t len) |
913 | 0 | { |
914 | 0 | unsigned select, elevation, cnr; |
915 | |
|
916 | 0 | if (4 != len) { |
917 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
918 | 0 | "Skytraq 0xB0: bad len %zu\n", len); |
919 | 0 | return 0; |
920 | 0 | } |
921 | | |
922 | 0 | select = getub(buf, 1); |
923 | 0 | elevation = getub(buf, 2); |
924 | 0 | cnr = getub(buf, 3); |
925 | |
|
926 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
927 | 0 | "Skytraq 0xB0: select %u el %u cnr %u\n", |
928 | 0 | select, elevation, cnr); |
929 | 0 | return 0; |
930 | 0 | } |
931 | | |
932 | | /* |
933 | | * decode MID 0xb4 - Position Pinning Status |
934 | | * |
935 | | * Present in Phoenix |
936 | | */ |
937 | | static gps_mask_t sky_msg_B4(struct gps_device_t *session, |
938 | | unsigned char *buf, size_t len) |
939 | 0 | { |
940 | 0 | unsigned status, pspeed, pcnt, uspeed, ucnt, udist; |
941 | |
|
942 | 0 | if (12 != len) { |
943 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
944 | 0 | "Skytraq 0xB4: bad len %zu\n", len); |
945 | 0 | return 0; |
946 | 0 | } |
947 | | |
948 | 0 | status = getub(buf, 1); |
949 | 0 | pspeed = getbeu16(buf, 2); |
950 | 0 | pcnt = getbeu16(buf, 4); |
951 | 0 | uspeed = getbeu16(buf, 6); |
952 | 0 | ucnt = getbeu16(buf, 8); |
953 | 0 | udist = getbeu16(buf, 10); |
954 | |
|
955 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
956 | 0 | "Skytraq 0xB4: status %u pspeed %u pcnt %u uspeed %u " |
957 | 0 | "ucnt %u udist %u\n", |
958 | 0 | status, pspeed, pcnt, uspeed, ucnt, udist); |
959 | 0 | return 0; |
960 | 0 | } |
961 | | |
962 | | /* |
963 | | * decode MID 0xB9 - Power Mode Status |
964 | | * |
965 | | * Present in Phoenix |
966 | | */ |
967 | | static gps_mask_t sky_msg_B9(struct gps_device_t *session, |
968 | | unsigned char *buf, size_t len) |
969 | 0 | { |
970 | 0 | unsigned mode; |
971 | |
|
972 | 0 | if (2 != len) { |
973 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
974 | 0 | "Skytraq 0xB9: bad len %zu\n", len); |
975 | 0 | return 0; |
976 | 0 | } |
977 | | |
978 | 0 | mode = getub(buf, 1); |
979 | |
|
980 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
981 | 0 | "Skytraq 0xB9: mode %u\n", mode); |
982 | 0 | return 0; |
983 | 0 | } |
984 | | |
985 | | /* |
986 | | * decode MID 0xBB - 1PPS Cable Delay |
987 | | * |
988 | | * Present in Phoenix |
989 | | */ |
990 | | static gps_mask_t sky_msg_BB(struct gps_device_t *session, |
991 | | unsigned char *buf, size_t len) |
992 | 0 | { |
993 | 0 | int delay; |
994 | |
|
995 | 0 | if (5 != len) { |
996 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
997 | 0 | "Skytraq 0xBB: bad len %zu\n", len); |
998 | 0 | return 0; |
999 | 0 | } |
1000 | | |
1001 | 0 | delay = getbeu32(buf, 1); |
1002 | |
|
1003 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1004 | 0 | "Skytraq 0xBB: delay %d\n", delay); |
1005 | 0 | return 0; |
1006 | 0 | } |
1007 | | |
1008 | | /* |
1009 | | * decode MID 0xDC, Measurement Time |
1010 | | * |
1011 | | * 10 bytes |
1012 | | */ |
1013 | | static gps_mask_t sky_msg_DC(struct gps_device_t *session, |
1014 | | unsigned char *buf, size_t len) |
1015 | 0 | { |
1016 | 0 | unsigned iod; // Issue of data 0 - 255 |
1017 | 0 | unsigned wn; // week number 0 - 65535 |
1018 | 0 | unsigned tow; // receiver tow 0 - 604799999 in mS |
1019 | 0 | unsigned mp; // measurement period 1 - 1000 ms |
1020 | 0 | char ts_buf[TIMESPEC_LEN]; |
1021 | 0 | timespec_t ts_tow; |
1022 | |
|
1023 | 0 | if (10 != len) { |
1024 | 0 | return 0; |
1025 | 0 | } |
1026 | | |
1027 | 0 | iod = (unsigned)getub(buf, 1); |
1028 | 0 | wn = getbeu16(buf, 2); |
1029 | 0 | tow = getbeu32(buf, 4); |
1030 | 0 | mp = getbeu16(buf, 8); |
1031 | 0 | MSTOTS(&ts_tow, tow); |
1032 | | |
1033 | | // should this be newdata.skyview_time? |
1034 | 0 | session->gpsdata.skyview_time = gpsd_gpstime_resolv(session, wn, ts_tow); |
1035 | |
|
1036 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1037 | 0 | "Skytraq 0xDC: iod %u wn %u tow %u mp %u t%s\n", |
1038 | 0 | iod, wn, tow, mp, |
1039 | 0 | timespec_str(&session->gpsdata.skyview_time, ts_buf, |
1040 | 0 | sizeof(ts_buf))); |
1041 | 0 | return 0; |
1042 | 0 | } |
1043 | | |
1044 | | /* |
1045 | | * decode MID 0xDD, Raw Measurements |
1046 | | * |
1047 | | */ |
1048 | | static gps_mask_t sky_msg_DD(struct gps_device_t *session, |
1049 | | unsigned char *buf, size_t len UNUSED) |
1050 | 0 | { |
1051 | 0 | unsigned iod; // Issue of data 0 - 255 |
1052 | 0 | unsigned nmeas; // number of measurements |
1053 | 0 | unsigned i; // generic loop variable |
1054 | |
|
1055 | 0 | iod = (unsigned)getub(buf, 1); |
1056 | 0 | nmeas = (unsigned)getub(buf, 2); |
1057 | |
|
1058 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1059 | 0 | "Skytraq 0xDD: iod=%u, nmeas=%u\n", |
1060 | 0 | iod, nmeas); |
1061 | | |
1062 | | // check IOD? |
1063 | 0 | session->gpsdata.raw.mtime = session->gpsdata.skyview_time; |
1064 | | |
1065 | | /* zero the measurement data |
1066 | | * so we can tell which meas never got set */ |
1067 | 0 | memset(session->gpsdata.raw.meas, 0, sizeof(session->gpsdata.raw.meas)); |
1068 | |
|
1069 | 0 | for (i = 0; i < nmeas; i++) { |
1070 | 0 | const char *obs_code; |
1071 | 0 | int off = 3 + (23 * i); |
1072 | |
|
1073 | 0 | uint8_t PRN = getub(buf, off + 0); |
1074 | | // carrier-to-noise density ratio dB-Hz |
1075 | 0 | uint8_t cno = getub(buf, off + 1); |
1076 | | // pseudorange in meters |
1077 | 0 | double prMes = getbed64((const char *)buf, off + 2); |
1078 | | // carrier phase in cycles |
1079 | 0 | double cpMes = getbed64((const char *)buf, off + 10); |
1080 | | // doppler in Hz, positive towards sat |
1081 | 0 | double doMes = getbef32((const char *)buf, off + 18); |
1082 | | |
1083 | | /* tracking stat |
1084 | | * bit 0 - prMes valid |
1085 | | * bit 1 - doppler valid |
1086 | | * bit 2 - cpMes valid |
1087 | | * bit 3 - cp slip |
1088 | | * bit 4 - Coherent integration time? |
1089 | | */ |
1090 | 0 | uint8_t trkStat = getub(buf, off + 22); |
1091 | 0 | uint8_t gnssId = 0; |
1092 | 0 | uint8_t svId = 0; |
1093 | 0 | PRN2_gnssId_svId(PRN, &gnssId, &svId); |
1094 | |
|
1095 | 0 | session->gpsdata.raw.meas[i].gnssid = gnssId; |
1096 | 0 | switch (gnssId) { |
1097 | 0 | case 0: // GPS |
1098 | 0 | FALLTHROUGH |
1099 | 0 | case 5: // QZSS |
1100 | 0 | FALLTHROUGH |
1101 | 0 | case 20: // IRNSS, just guessing here |
1102 | 0 | obs_code = "L1C"; // u-blox calls this L1C/A ? |
1103 | 0 | break; |
1104 | 0 | case 1: // SBAS |
1105 | 0 | svId -= 100; // adjust for RINEX 3 svid |
1106 | 0 | obs_code = "L1C"; // u-blox calls this L1C/A |
1107 | 0 | break; |
1108 | 0 | case 2: // GALILEO |
1109 | 0 | obs_code = "L1B"; // u-blox calls this E1OS |
1110 | 0 | break; |
1111 | 0 | case 3: // BeiDou |
1112 | 0 | obs_code = "L2I"; // u-blox calls this B1I |
1113 | 0 | break; |
1114 | 0 | default: // huh? |
1115 | 0 | FALLTHROUGH |
1116 | 0 | case 4: // IMES. really? |
1117 | 0 | obs_code = ""; // u-blox calls this L1 |
1118 | 0 | break; |
1119 | 0 | case 6: // GLONASS |
1120 | 0 | obs_code = "L1C"; // u-blox calls this L1OF |
1121 | 0 | break; |
1122 | 0 | } |
1123 | 0 | (void)strlcpy(session->gpsdata.raw.meas[i].obs_code, obs_code, |
1124 | 0 | sizeof(session->gpsdata.raw.meas[i].obs_code)); |
1125 | |
|
1126 | 0 | session->gpsdata.raw.meas[i].svid = svId; |
1127 | 0 | session->gpsdata.raw.meas[i].snr = cno; |
1128 | 0 | session->gpsdata.raw.meas[i].satstat = trkStat; |
1129 | 0 | if (trkStat & 1) { |
1130 | | // prMes valid |
1131 | 0 | session->gpsdata.raw.meas[i].pseudorange = prMes; |
1132 | 0 | } else { |
1133 | 0 | session->gpsdata.raw.meas[i].pseudorange = NAN; |
1134 | 0 | } |
1135 | 0 | if (trkStat & 2) { |
1136 | | // doppler valid |
1137 | 0 | session->gpsdata.raw.meas[i].doppler = doMes; |
1138 | 0 | } else { |
1139 | 0 | session->gpsdata.raw.meas[i].doppler = NAN; |
1140 | 0 | } |
1141 | 0 | if (trkStat & 4) { |
1142 | | // cpMes valid |
1143 | 0 | session->gpsdata.raw.meas[i].carrierphase = cpMes; |
1144 | 0 | } else { |
1145 | 0 | session->gpsdata.raw.meas[i].carrierphase = NAN; |
1146 | 0 | } |
1147 | 0 | session->gpsdata.raw.meas[i].codephase = NAN; |
1148 | 0 | session->gpsdata.raw.meas[i].deltarange = NAN; |
1149 | | // skytraq does not report locktime, so assume max |
1150 | 0 | session->gpsdata.raw.meas[i].locktime = LOCKMAX; |
1151 | 0 | if (trkStat & 8) { |
1152 | | // possible slip |
1153 | 0 | session->gpsdata.raw.meas[i].lli = 2; |
1154 | 0 | } |
1155 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1156 | 0 | "PRN %u (%u:%u) prMes %f cpMes %f doMes %f\n" |
1157 | 0 | "cno %u rtkStat %u\n", PRN, |
1158 | 0 | gnssId, svId, prMes, cpMes, doMes, cno, trkStat); |
1159 | |
|
1160 | 0 | } |
1161 | | |
1162 | | // return RAW_IS; // WIP |
1163 | 0 | return 0; |
1164 | 0 | } |
1165 | | |
1166 | | /* |
1167 | | * decode MID 0xDE, SV and channel status |
1168 | | * |
1169 | | * max payload: 3 + (Num_sats * 10) = 483 bytes |
1170 | | */ |
1171 | | static gps_mask_t sky_msg_DE(struct gps_device_t *session, |
1172 | | unsigned char *buf, size_t len UNUSED) |
1173 | 0 | { |
1174 | 0 | int st, nsv; |
1175 | 0 | unsigned i; |
1176 | 0 | unsigned iod; // Issue of data 0 - 255 |
1177 | 0 | unsigned nsvs; // number of SVs in this packet |
1178 | |
|
1179 | 0 | iod = (unsigned)getub(buf, 1); |
1180 | 0 | nsvs = (unsigned)getub(buf, 2); |
1181 | | // too many sats? |
1182 | 0 | if (SKY_CHANNELS < nsvs) { |
1183 | 0 | return 0; |
1184 | 0 | } |
1185 | | |
1186 | 0 | gpsd_zero_satellites(&session->gpsdata); |
1187 | 0 | for (i = st = nsv = 0; i < nsvs; i++) { |
1188 | 0 | int off = 3 + (10 * i); // offset into buffer of start of this sat |
1189 | 0 | bool good; // do we have a good record ? |
1190 | 0 | unsigned short sv_stat; |
1191 | 0 | unsigned short chan_stat; |
1192 | 0 | unsigned short ura; |
1193 | 0 | short PRN; |
1194 | 0 | uint8_t gnssId = 0; |
1195 | 0 | uint8_t svId = 0; |
1196 | |
|
1197 | 0 | PRN = (short)getub(buf, off + 1); |
1198 | | // fit into gnssid:svid |
1199 | 0 | if (0 == PRN) { |
1200 | | // skip 0 PRN |
1201 | 0 | continue; |
1202 | 0 | } |
1203 | 0 | PRN2_gnssId_svId(PRN, &gnssId, &svId); |
1204 | |
|
1205 | 0 | session->gpsdata.skyview[st].gnssid = gnssId; |
1206 | 0 | session->gpsdata.skyview[st].svid = svId; |
1207 | 0 | session->gpsdata.skyview[st].PRN = PRN; |
1208 | |
|
1209 | 0 | sv_stat = (unsigned short)getub(buf, off + 2); |
1210 | 0 | ura = (unsigned short)getub(buf, off + 3); |
1211 | 0 | session->gpsdata.skyview[st].ss = (double)getub(buf, off + 4); |
1212 | 0 | session->gpsdata.skyview[st].elevation = |
1213 | 0 | (double)getbes16(buf, off + 5); |
1214 | 0 | session->gpsdata.skyview[st].azimuth = |
1215 | 0 | (double)getbes16(buf, off + 7); |
1216 | 0 | chan_stat = (unsigned short)getub(buf, off + 9); |
1217 | |
|
1218 | 0 | session->gpsdata.skyview[st].used = (bool)(chan_stat & 0x30); |
1219 | 0 | good = session->gpsdata.skyview[st].PRN != 0 && |
1220 | 0 | session->gpsdata.skyview[st].azimuth != 0 && |
1221 | 0 | session->gpsdata.skyview[st].elevation != 0; |
1222 | |
|
1223 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1224 | 0 | "Skytraq PRN=%2d El=%4.0f Az=%5.0f ss=%3.2f stat=%02x,%02x " |
1225 | 0 | "ura=%d %c\n", |
1226 | 0 | session->gpsdata.skyview[st].PRN, |
1227 | 0 | session->gpsdata.skyview[st].elevation, |
1228 | 0 | session->gpsdata.skyview[st].azimuth, |
1229 | 0 | session->gpsdata.skyview[st].ss, |
1230 | 0 | chan_stat, sv_stat, ura, |
1231 | 0 | good ? '*' : ' '); |
1232 | |
|
1233 | 0 | if (good) { |
1234 | 0 | st += 1; |
1235 | 0 | if (session->gpsdata.skyview[st].used) { |
1236 | 0 | nsv++; |
1237 | 0 | } |
1238 | 0 | } |
1239 | 0 | } |
1240 | |
|
1241 | 0 | session->gpsdata.satellites_visible = st; |
1242 | 0 | session->gpsdata.satellites_used = nsv; |
1243 | |
|
1244 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1245 | 0 | "Skytraq 0xDE: nsvs=%u visible=%u iod=%u\n", nsvs, |
1246 | 0 | session->gpsdata.satellites_visible, iod); |
1247 | 0 | return SATELLITE_SET | USED_IS; |
1248 | 0 | } |
1249 | | |
1250 | | /* |
1251 | | * decode MID 0xDF, Nav status (PVT) |
1252 | | * |
1253 | | * 81 bytes |
1254 | | */ |
1255 | | static gps_mask_t sky_msg_DF(struct gps_device_t *session, |
1256 | | unsigned char *buf, size_t len) |
1257 | 0 | { |
1258 | 0 | unsigned short navstat; |
1259 | 0 | unsigned iod; // Issue of data 0 - 255 |
1260 | 0 | unsigned wn; // week number 0 - 65535 |
1261 | 0 | double f_tow; // receiver tow Sec |
1262 | 0 | double clock_bias; |
1263 | 0 | double clock_drift; |
1264 | 0 | gps_mask_t mask = 0; |
1265 | 0 | timespec_t ts_tow; |
1266 | 0 | char ts_buf[TIMESPEC_LEN]; |
1267 | |
|
1268 | 0 | if (81 != len) { |
1269 | 0 | return 0; |
1270 | 0 | } |
1271 | | |
1272 | 0 | iod = (unsigned)getub(buf, 1); |
1273 | | |
1274 | | // fix status is byte 2 |
1275 | 0 | navstat = (unsigned short)getub(buf, 2); |
1276 | 0 | session->newdata.status = STATUS_UNK; |
1277 | 0 | session->newdata.mode = MODE_NO_FIX; |
1278 | 0 | switch (navstat) { |
1279 | 0 | case 1: |
1280 | | // fix prediction, ignore |
1281 | 0 | break; |
1282 | 0 | case 2: |
1283 | 0 | session->newdata.status = STATUS_GPS; |
1284 | 0 | session->newdata.mode = MODE_2D; |
1285 | 0 | break; |
1286 | 0 | case 3: |
1287 | 0 | session->newdata.status = STATUS_GPS; |
1288 | 0 | session->newdata.mode = MODE_3D; |
1289 | 0 | break; |
1290 | 0 | case 4: |
1291 | 0 | session->newdata.status = STATUS_DGPS; |
1292 | 0 | session->newdata.mode = MODE_3D; |
1293 | 0 | break; |
1294 | 0 | default: |
1295 | 0 | break; |
1296 | 0 | } |
1297 | | |
1298 | 0 | wn = getbeu16(buf, 3); |
1299 | 0 | f_tow = getbed64((const char *)buf, 5); |
1300 | 0 | DTOTS(&ts_tow, f_tow); |
1301 | | |
1302 | | // position/velocity is bytes 13-48, meters and m/s |
1303 | 0 | session->newdata.ecef.x = (double)getbed64((const char *)buf, 13), |
1304 | 0 | session->newdata.ecef.y = (double)getbed64((const char *)buf, 21), |
1305 | 0 | session->newdata.ecef.z = (double)getbed64((const char *)buf, 29), |
1306 | 0 | session->newdata.ecef.vx = (double)getbef32((const char *)buf, 37), |
1307 | 0 | session->newdata.ecef.vy = (double)getbef32((const char *)buf, 41), |
1308 | 0 | session->newdata.ecef.vz = (double)getbef32((const char *)buf, 45); |
1309 | 0 | mask |= ECEF_SET | VECEF_SET; |
1310 | |
|
1311 | 0 | clock_bias = getbed64((const char *)buf, 49); |
1312 | 0 | clock_drift = getbes32(buf, 57); |
1313 | |
|
1314 | 0 | session->gpsdata.dop.gdop = getbef32((const char *)buf, 61); |
1315 | 0 | session->gpsdata.dop.pdop = getbef32((const char *)buf, 65); |
1316 | 0 | session->gpsdata.dop.hdop = getbef32((const char *)buf, 69); |
1317 | 0 | session->gpsdata.dop.vdop = getbef32((const char *)buf, 73); |
1318 | 0 | session->gpsdata.dop.tdop = getbef32((const char *)buf, 77); |
1319 | 0 | mask |= DOP_SET; |
1320 | |
|
1321 | 0 | session->newdata.time = gpsd_gpstime_resolv(session, wn, ts_tow ); |
1322 | |
|
1323 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1324 | 0 | "Skytraq 0xDF: iod=%u, stat=%u, wn=%u, tow=%f, t=%s " |
1325 | 0 | "cb: %f, cd: %f " |
1326 | 0 | "gdop: %.2f, pdop: %.2f, hdop: %.2f, vdop: %.2f, tdop: %.2f\n", |
1327 | 0 | iod, navstat, wn, f_tow, |
1328 | 0 | timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)), |
1329 | 0 | clock_bias, clock_drift, |
1330 | 0 | session->gpsdata.dop.gdop, |
1331 | 0 | session->gpsdata.dop.pdop, |
1332 | 0 | session->gpsdata.dop.hdop, |
1333 | 0 | session->gpsdata.dop.vdop, |
1334 | 0 | session->gpsdata.dop.tdop); |
1335 | |
|
1336 | 0 | mask |= TIME_SET | STATUS_SET | MODE_SET | CLEAR_IS | REPORT_IS; |
1337 | 0 | return mask; |
1338 | 0 | } |
1339 | | |
1340 | | /* |
1341 | | * decode MID 0xE0, GPS Subframe data |
1342 | | * |
1343 | | * len 33 bytes |
1344 | | * |
1345 | | */ |
1346 | | static gps_mask_t sky_msg_E0(struct gps_device_t *session, |
1347 | | unsigned char *buf, size_t len UNUSED) |
1348 | 0 | { |
1349 | 0 | int i; |
1350 | 0 | unsigned prn; // GPS sat PRN |
1351 | 0 | unsigned subf; // subframe 1-5 |
1352 | | // the words are preprocessed, not raw, just the 24bits of data |
1353 | 0 | uint32_t words[10]; // subframe 1-5 |
1354 | |
|
1355 | 0 | if (33 != len) { |
1356 | 0 | return 0; |
1357 | 0 | } |
1358 | | |
1359 | 0 | prn = (unsigned)getub(buf, 1); |
1360 | 0 | subf = (unsigned)getub(buf, 2); |
1361 | 0 | for (i = 0; i < 10; i++) { |
1362 | 0 | words[i] = (uint32_t)getbeu24(buf, 3 + (i * 3)); |
1363 | 0 | } |
1364 | |
|
1365 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1366 | 0 | "Skytraq 0xE0: prn=%u, subf=%u\n", |
1367 | 0 | prn, subf); |
1368 | | |
1369 | | // could be SBAS? |
1370 | 0 | return gpsd_interpret_subframe(session, GNSSID_GPS, prn, words); |
1371 | 0 | } |
1372 | | |
1373 | | /* |
1374 | | * pretend to decode MID 0xE2, Beiduo D1 Subframe data |
1375 | | * |
1376 | | * from Beidou Standard BDS-SIS-ICD-2.0 |
1377 | | * D1, with the data rate of 50 bps, is broadcasted by the MEO/IGSO satellites |
1378 | | * |
1379 | | * len 31 bytes |
1380 | | * |
1381 | | */ |
1382 | | static gps_mask_t sky_msg_E2(struct gps_device_t *session, |
1383 | | unsigned char *buf, size_t len) |
1384 | 0 | { |
1385 | 0 | int i; |
1386 | 0 | unsigned prn; // BeidouPS sat PRN 206-214 |
1387 | 0 | unsigned subf; // subframe 1-5 |
1388 | | // the words are preprocessed, not raw, just the 28 bytes of data |
1389 | 0 | uint8_t bytes[28]; // raw data |
1390 | |
|
1391 | 0 | if (31 != len) { |
1392 | 0 | return 0; |
1393 | 0 | } |
1394 | | |
1395 | 0 | prn = (unsigned)getub(buf, 1); |
1396 | 0 | subf = (unsigned)getub(buf, 2); |
1397 | 0 | for (i = 0; i < 28; i++) { |
1398 | 0 | bytes[i] = getub(buf, 3 + i); |
1399 | 0 | } |
1400 | |
|
1401 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1402 | 0 | "Skytraq Beidou D1 subframe PRN %d Subframe %d " |
1403 | 0 | "length %zd byte:%s\n", |
1404 | 0 | prn, subf, len, |
1405 | 0 | gps_hexdump(session->msgbuf, sizeof(session->msgbuf), |
1406 | 0 | bytes, 28)); |
1407 | |
|
1408 | 0 | return ONLINE_SET; |
1409 | 0 | } |
1410 | | |
1411 | | /* |
1412 | | * pretend to decode MID 0xE3, Beiduo D2 Subframe data |
1413 | | * |
1414 | | * from Beidou Standard BDS-SIS-ICD-2.0 |
1415 | | * D2, with the data rate of 500 bps, is broadcasted by the GEO satellites. |
1416 | | * |
1417 | | * len 31 bytes |
1418 | | * |
1419 | | */ |
1420 | | static gps_mask_t sky_msg_E3(struct gps_device_t *session, |
1421 | | unsigned char *buf, size_t len) |
1422 | 0 | { |
1423 | 0 | int i; |
1424 | 0 | unsigned prn; // BeidouPS sat PRN 201-205 |
1425 | 0 | unsigned subf; // subframe 1-5 |
1426 | | // the words are preprocessed, not raw, just the 28 bytes of data |
1427 | 0 | uint8_t bytes[28]; // raw data |
1428 | |
|
1429 | 0 | if (31 != len) { |
1430 | 0 | return 0; |
1431 | 0 | } |
1432 | | |
1433 | 0 | prn = (unsigned)getub(buf, 1); |
1434 | 0 | subf = (unsigned)getub(buf, 2); |
1435 | 0 | for (i = 0; i < 28; i++) { |
1436 | 0 | bytes[i] = getub(buf, 3 + i); |
1437 | 0 | } |
1438 | |
|
1439 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1440 | 0 | "Skytraq Beidou D2 subframe PRN %d Subframe %d " |
1441 | 0 | "length %zd byte:%s\n", |
1442 | 0 | prn, subf, len, |
1443 | 0 | gps_hexdump(session->msgbuf, sizeof(session->msgbuf), |
1444 | 0 | bytes, 28)); |
1445 | |
|
1446 | 0 | return ONLINE_SET; |
1447 | 0 | } |
1448 | | |
1449 | | |
1450 | | static gps_mask_t sky_parse(struct gps_device_t * session, unsigned char *buf, |
1451 | | size_t len) |
1452 | 0 | { |
1453 | 0 | gps_mask_t mask = 0; |
1454 | |
|
1455 | 0 | if (0 == len) { |
1456 | 0 | return mask; |
1457 | 0 | } |
1458 | | |
1459 | 0 | buf += 4; // skip the leaders and length |
1460 | 0 | len -= 7; // don't count the leaders, length, csum and terminators |
1461 | | // session->driver.sirf.lastid = buf[0]; |
1462 | | |
1463 | | // check the checksum?? |
1464 | | |
1465 | | // could change if the set of messages we enable does |
1466 | | // session->cycle_end_reliable = true; |
1467 | |
|
1468 | 0 | switch (buf[0]) { |
1469 | 0 | case 0x62: |
1470 | 0 | mask = sky_msg_62(session, buf, len); |
1471 | 0 | break; |
1472 | 0 | case 0x63: |
1473 | 0 | mask = sky_msg_63(session, buf, len); |
1474 | 0 | break; |
1475 | 0 | case 0x64: |
1476 | 0 | mask = sky_msg_64(session, buf, len); |
1477 | 0 | break; |
1478 | 0 | case 0x65: |
1479 | 0 | mask = sky_msg_65(session, buf, len); |
1480 | 0 | break; |
1481 | 0 | case 0x6A: |
1482 | 0 | mask = sky_msg_6A(session, buf, len); |
1483 | 0 | break; |
1484 | 0 | case 0x7A: |
1485 | 0 | mask = sky_msg_7A(session, buf, len); |
1486 | 0 | break; |
1487 | 0 | case 0x80: |
1488 | | // 128 |
1489 | 0 | mask = sky_msg_80(session, buf, len); |
1490 | 0 | break; |
1491 | 0 | case 0x81: |
1492 | | // Software CRC |
1493 | 0 | mask = sky_msg_81(session, buf, len); |
1494 | 0 | break; |
1495 | 0 | case 0x83: |
1496 | | // 131 - ACK |
1497 | 0 | if (2 == len) { |
1498 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1499 | 0 | "Skytraq 0x83: ACK MID %#02x\n", buf[1]); |
1500 | 0 | } else if (3 == len) { |
1501 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1502 | 0 | "Skytraq 0x83: ACK MID %#02x/%02x\n", buf[1], buf[2]); |
1503 | 0 | } else if (4 <= len) { |
1504 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1505 | 0 | "Skytraq 0x83: ACK MID %#02x/%02x/%02x\n", |
1506 | 0 | buf[1], buf[2], buf[3]); |
1507 | 0 | } else { |
1508 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1509 | 0 | "Skytraq 0x83: ACK\n"); |
1510 | 0 | } |
1511 | 0 | break; |
1512 | 0 | case 0x84: |
1513 | | // 132 - NACK |
1514 | 0 | if (2 == len) { |
1515 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
1516 | 0 | "Skytraq 0x84: NACK MID %#02x\n", buf[1]); |
1517 | 0 | } else if (3 == len) { |
1518 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
1519 | 0 | "Skytraq 0x84: NACK MID %#02x/%02x\n", buf[1], buf[2]); |
1520 | 0 | } else if (4 <= len) { |
1521 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
1522 | 0 | "Skytraq 0x84: NACK MID %#02x/%02x/x%02x\n", |
1523 | 0 | buf[1], buf[2], buf[3]); |
1524 | 0 | } else { |
1525 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
1526 | 0 | "Skytraq 0x84: NACK\n"); |
1527 | 0 | } |
1528 | 0 | break; |
1529 | 0 | case 0x86: |
1530 | | // 134 Position Update Rate |
1531 | 0 | mask = sky_msg_86(session, buf, len); |
1532 | 0 | break; |
1533 | 0 | case 0x89: |
1534 | 0 | mask = sky_msg_89(session, buf, len); |
1535 | 0 | break; |
1536 | 0 | case 0x8A: |
1537 | 0 | mask = sky_msg_8A(session, buf, len); |
1538 | 0 | break; |
1539 | 0 | case 0x8B: |
1540 | 0 | mask = sky_msg_8B(session, buf, len); |
1541 | 0 | break; |
1542 | 0 | case 0x93: |
1543 | | // NMEA TALKER id |
1544 | 0 | mask = sky_msg_93(session, buf, len); |
1545 | 0 | break; |
1546 | | |
1547 | 0 | case 0xA8: |
1548 | | // Navigation Data Message |
1549 | 0 | mask = sky_msg_A8(session, buf, len); |
1550 | 0 | break; |
1551 | | |
1552 | 0 | case 0xae: |
1553 | | // GNSS Datum |
1554 | 0 | mask = sky_msg_AE(session, buf, len); |
1555 | 0 | break; |
1556 | | |
1557 | 0 | case 0xaf: |
1558 | | // DOP Mask |
1559 | 0 | mask = sky_msg_AF(session, buf, len); |
1560 | 0 | break; |
1561 | | |
1562 | 0 | case 0xb0: |
1563 | | // Elevation and CNR mask |
1564 | 0 | mask = sky_msg_B0(session, buf, len); |
1565 | 0 | break; |
1566 | | |
1567 | 0 | case 0xb4: |
1568 | | // Positoin Pinning Status |
1569 | 0 | mask = sky_msg_B4(session, buf, len); |
1570 | 0 | break; |
1571 | | |
1572 | 0 | case 0xb9: |
1573 | 0 | mask = sky_msg_B9(session, buf, len); |
1574 | 0 | break; |
1575 | | |
1576 | 0 | case 0xbb: |
1577 | 0 | mask = sky_msg_BB(session, buf, len); |
1578 | 0 | break; |
1579 | | |
1580 | 0 | case 0xDC: |
1581 | | // 220 |
1582 | 0 | mask = sky_msg_DC(session, buf, len); |
1583 | 0 | break; |
1584 | | |
1585 | 0 | case 0xDD: |
1586 | | // 221 |
1587 | 0 | mask = sky_msg_DD(session, buf, len); |
1588 | 0 | break; |
1589 | | |
1590 | 0 | case 0xDE: |
1591 | | // 222 |
1592 | 0 | mask = sky_msg_DE(session, buf, len); |
1593 | 0 | break; |
1594 | | |
1595 | 0 | case 0xDF: |
1596 | | // 223 - Nave status (PVT) |
1597 | 0 | mask = sky_msg_DF(session, buf, len); |
1598 | 0 | break; |
1599 | | |
1600 | 0 | case 0xE0: |
1601 | | // 224 |
1602 | 0 | mask = sky_msg_E0(session, buf, len); |
1603 | 0 | break; |
1604 | | |
1605 | 0 | case 0xE2: |
1606 | | // 226 - Beidou2 D1 Subframe data |
1607 | 0 | mask = sky_msg_E2(session, buf, len); |
1608 | 0 | break; |
1609 | | |
1610 | 0 | case 0xE3: |
1611 | | // 227 - Beidou2 D2 Subframe data |
1612 | 0 | mask = sky_msg_E3(session, buf, len); |
1613 | 0 | break; |
1614 | | |
1615 | 0 | case 0x67: // sub-id messages |
1616 | 0 | FALLTHROUGH |
1617 | 0 | case 0x6F: // sub-id messages |
1618 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1619 | 0 | "Skytraq Unknown MID x%02x SID x%02x length %zd\n", |
1620 | 0 | buf[0], buf[1], len); |
1621 | 0 | break; |
1622 | 0 | default: |
1623 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1624 | 0 | "Skytraq Unknown MID %#02x length %zd\n", |
1625 | 0 | buf[0], len); |
1626 | 0 | break; |
1627 | 0 | } |
1628 | 0 | return mask; |
1629 | 0 | } |
1630 | | |
1631 | | static gps_mask_t skybin_parse_input(struct gps_device_t *session) |
1632 | 0 | { |
1633 | | /* |
1634 | | * Use this hook to step, slowly, through the init messages. |
1635 | | * By sending only one for each three received we try |
1636 | | * to avoid overrunning the receiver input buffer. |
1637 | | */ |
1638 | |
|
1639 | 0 | if (UINT_MAX != session->cfg_stage) { |
1640 | 0 | session->cfg_step++; |
1641 | 0 | } |
1642 | |
|
1643 | 0 | if (UINT_MAX != session->cfg_stage && |
1644 | 0 | 3 <= session->cfg_step) { |
1645 | | // more init to do |
1646 | |
|
1647 | 0 | session->cfg_stage++; |
1648 | 0 | session->cfg_step = 0; |
1649 | | |
1650 | | // Note: the checksums in the Skytaq doc are sometimes wrong... |
1651 | | |
1652 | | // drivers/driver_nmea0183.c send 0x04 to get MID 0x80 on detect |
1653 | | |
1654 | | // FIXME: make a table |
1655 | 0 | switch (session->cfg_stage) { |
1656 | 0 | case 1: |
1657 | | // Send MID 0x3, to get back MID 0x81 Software CRC |
1658 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x02\x03\x00\x03\x0d\x0a", |
1659 | 0 | 9); |
1660 | 0 | break; |
1661 | 0 | case 2: |
1662 | | // Send MID 0x10, to get back MID 0x86 (Position Update Rate) |
1663 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x10\x10\x0d\x0a", 8); |
1664 | 0 | break; |
1665 | 0 | case 3: |
1666 | | // Send MID 0x15, to get back MID 0xB9 (Power Mode Status) |
1667 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x15\x15\x0d\x0a", 8); |
1668 | 0 | break; |
1669 | 0 | case 4: |
1670 | | // Send MID 0x1f, to get back MID 0x89 Measurement data statuS |
1671 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x1f\x1f\x0d\x0a", 8); |
1672 | 0 | break; |
1673 | 0 | case 5: |
1674 | | // Send MID 0x21, to get back MID 0x8a RTCM Data output status |
1675 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x21\x21\x0d\x0a", 8); |
1676 | 0 | break; |
1677 | 0 | case 6: |
1678 | | // Send MID 0x23, to get back MID 0x8B (Base Position) |
1679 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x23\x23\x0d\x0a", 8); |
1680 | 0 | break; |
1681 | 0 | case 7: |
1682 | | // Send MID 0x2d, to get back MID 0xAE (GNSS Datum) |
1683 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x2d\x2d\x0d\x0a", 8); |
1684 | 0 | break; |
1685 | 0 | case 8: |
1686 | | // Send MID 0x2E, to get back MID 0xAF (DOP Mask) |
1687 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x2e\x2e\x0d\x0a", 8); |
1688 | 0 | break; |
1689 | 0 | case 9: |
1690 | | // Send MID 0x2F, to get back MID 0x80 Elevation and SNR mask |
1691 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x2f\x2f\x0d\x0a", 8); |
1692 | 0 | break; |
1693 | 0 | case 10: |
1694 | | // Send MID 0x3a, to get back MID 0xb4 Position Pinning |
1695 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x3a\x3a\x0d\x0a", 8); |
1696 | 0 | break; |
1697 | 0 | case 11: |
1698 | | // Send MID 0x44, to get back MID 0xc2 1PPS timing |
1699 | | // Timing mode versions only |
1700 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x44\x44\x0d\x0a", 8); |
1701 | 0 | break; |
1702 | 0 | case 12: |
1703 | | // Send MID 0x46, to get back MID 0xbb 1PPS delay |
1704 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x46\x46\x0d\x0a", 8); |
1705 | 0 | break; |
1706 | 0 | case 13: |
1707 | | // Send MID 0x4f, to get back MID 0x93 NMEA talker ID |
1708 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x4f\x4f\x0d\x0a", 8); |
1709 | 0 | break; |
1710 | 0 | case 14: |
1711 | | // Send MID 0x56, to get back MID 0xc3 1PPS Output Mode |
1712 | | // Timing mode versions only |
1713 | 0 | (void)sky_write(session, "\xA0\xA1\x00\x01\x56\x56\x0d\x0a", 8); |
1714 | 0 | break; |
1715 | 0 | case 15: |
1716 | | // Send MID 0x62/02, to get back MID 0x62/80 SBAS status |
1717 | 0 | (void)sky_write(session, |
1718 | 0 | "\xA0\xA1\x00\x02\x62\x02\x60\x0d\x0a", 9); |
1719 | 0 | break; |
1720 | 0 | case 16: |
1721 | | // Send MID 0x62/04, to get back MID 0x62/81 QZSS status |
1722 | 0 | (void)sky_write(session, |
1723 | 0 | "\xA0\xA1\x00\x02\x62\x04\x66\x0d\x0a", 9); |
1724 | 0 | break; |
1725 | 0 | case 17: |
1726 | | // Send MID 0x62/06, to get back MID 0x62/82 SBAS Advanced status |
1727 | 0 | (void)sky_write(session, |
1728 | 0 | "\xA0\xA1\x00\x02\x62\x06\x64\x0d\x0a", 9); |
1729 | 0 | break; |
1730 | 0 | case 18: |
1731 | | // Send MID 0x63/02, to get back MID 0x62/80 SAEE Status |
1732 | | // not on PX1172RH_DS |
1733 | 0 | (void)sky_write(session, |
1734 | 0 | "\xA0\xA1\x00\x02\x63\x02\x61\x0d\x0a", 9); |
1735 | 0 | break; |
1736 | 0 | case 19: |
1737 | | // Send MID 0x64/01, to get back MID 0x64/80 |
1738 | 0 | (void)sky_write(session, |
1739 | 0 | "\xA0\xA1\x00\x02\x64\x01\x65\x0d\x0a", 9); |
1740 | 0 | break; |
1741 | 0 | case 20: |
1742 | | // Send MID 0x64/03, to get back MID 0x64/81 |
1743 | 0 | (void)sky_write(session, |
1744 | 0 | "\xA0\xA1\x00\x02\x64\x03\x67\x0d\x0a", 9); |
1745 | 0 | break; |
1746 | 0 | case 21: |
1747 | | // Send MID 0x64/07, to get back MID 0x64/83 |
1748 | 0 | (void)sky_write(session, |
1749 | 0 | "\xA0\xA1\x00\x02\x64\x07\x63\x0d\x0a", 9); |
1750 | 0 | break; |
1751 | 0 | case 22: |
1752 | | // Send MID 0x64/0b, to get back MID 0x64/85 |
1753 | 0 | (void)sky_write(session, |
1754 | 0 | "\xA0\xA1\x00\x02\x64\x0b\x6f\x0d\x0a", 9); |
1755 | 0 | break; |
1756 | 0 | case 23: |
1757 | | // Send MID 0x64/12, to get back MID 0x64/88 |
1758 | 0 | (void)sky_write(session, |
1759 | 0 | "\xA0\xA1\x00\x02\x64\x12\x76\x0d\x0a", 9); |
1760 | 0 | break; |
1761 | 0 | case 24: |
1762 | | // Send MID 0x64/16, to get back MID 0x64/8a |
1763 | 0 | (void)sky_write(session, |
1764 | 0 | "\xA0\xA1\x00\x02\x64\x16\x72\x0d\x0a", 9); |
1765 | 0 | break; |
1766 | 0 | case 25: |
1767 | | // Send MID 0x64/18, to get back MID 0x64/8b |
1768 | 0 | (void)sky_write(session, |
1769 | 0 | "\xA0\xA1\x00\x02\x64\x18\x7c\x0d\x0a", 9); |
1770 | 0 | break; |
1771 | 0 | case 26: |
1772 | | // Send MID 0x64/1a, to get back MID 0x64/8c |
1773 | 0 | (void)sky_write(session, |
1774 | 0 | "\xA0\xA1\x00\x02\x64\x1a\x7e\x0d\x0a", 9); |
1775 | 0 | break; |
1776 | 0 | case 27: |
1777 | | // Send MID 0x64/20, to get back MID 0x64/8e |
1778 | 0 | (void)sky_write(session, |
1779 | 0 | "\xA0\xA1\x00\x02\x64\x20\x44\x0d\x0a", 9); |
1780 | 0 | break; |
1781 | 0 | case 28: |
1782 | | // Send MID 0x64/22, to get back MID 0x64/8f |
1783 | | // not on PX1172RH_DS |
1784 | 0 | (void)sky_write(session, |
1785 | 0 | "\xA0\xA1\x00\x02\x64\x22\x46\x0d\x0a", 9); |
1786 | 0 | break; |
1787 | 0 | case 29: |
1788 | | // Send MID 0x64/28, to get back MID 0x64/92 |
1789 | 0 | (void)sky_write(session, |
1790 | 0 | "\xA0\xA1\x00\x02\x64\x28\x4c\x0d\x0a", 9); |
1791 | 0 | break; |
1792 | 0 | case 30: |
1793 | | // Send MID 0x64/30, to get back MID 0x64/98 |
1794 | | // not on PX1172RH_DS |
1795 | 0 | (void)sky_write(session, |
1796 | 0 | "\xA0\xA1\x00\x02\x64\x30\x54\x0d\x0a", 9); |
1797 | 0 | break; |
1798 | 0 | case 31: |
1799 | | // Send MID 0x64/31, to get back MID 0x64/ |
1800 | | // not on PX1172RH_DS |
1801 | 0 | (void)sky_write(session, |
1802 | 0 | "\xA0\xA1\x00\x02\x64\x31\x55\x0d\x0a", 9); |
1803 | 0 | break; |
1804 | 0 | case 32: |
1805 | | // Send MID 0x64/7d, to get back MID 0x64/fe |
1806 | 0 | (void)sky_write(session, |
1807 | 0 | "\xA0\xA1\x00\x02\x64\x7d\x19\x0d\x0a", 9); |
1808 | 0 | break; |
1809 | 0 | case 33: |
1810 | | // Send MID 0x64/35, to get back MID 0x64/99 |
1811 | | // not on PX1172RH_DS |
1812 | 0 | (void)sky_write(session, |
1813 | 0 | "\xA0\xA1\x00\x03\x64\x35\x01\x50\x0d\x0a", 10); |
1814 | 0 | break; |
1815 | 0 | case 34: |
1816 | | // Send MID 0x64/36, to get back MID 0x64/9a |
1817 | | // not on PX1172RH_DS |
1818 | 0 | (void)sky_write(session, |
1819 | 0 | "\xA0\xA1\x00\x02\x64\x36\x52\x0d\x0a", 9); |
1820 | 0 | break; |
1821 | 0 | case 35: |
1822 | | // Send MID 0x64/3c, to get back MID 0x64/99 |
1823 | | // not on PX1172RH_DS |
1824 | 0 | (void)sky_write(session, |
1825 | 0 | "\xA0\xA1\x00\x04\x64\x3c\x47\x47\x19\x0d\x0a", |
1826 | 0 | 11); |
1827 | 0 | break; |
1828 | 0 | case 36: |
1829 | | // Send MID 0x65/02, to get back MID 0x64/80 |
1830 | 0 | (void)sky_write(session, |
1831 | 0 | "\xA0\xA1\x00\x02\x65\x02\x67\x0d\x0a", 9); |
1832 | 0 | break; |
1833 | 0 | case 37: |
1834 | | // Send MID 0x65/04, to get back MID 0x64/8f |
1835 | 0 | (void)sky_write(session, |
1836 | 0 | "\xA0\xA1\x00\x02\x65\x04\x61\x0d\x0a", 9); |
1837 | 0 | break; |
1838 | 0 | case 38: |
1839 | | // Send MID 0x6a/02, to get back MID 0x6a/83 |
1840 | 0 | (void)sky_write(session, |
1841 | 0 | "\xA0\xA1\x00\x02\x6a\x02\x68\x0d\x0a", 9); |
1842 | 0 | break; |
1843 | 0 | case 39: |
1844 | | // Send MID 0x6a/07, to get back MID 0x6a/83 |
1845 | 0 | (void)sky_write(session, |
1846 | 0 | "\xA0\xA1\x00\x02\x6a\x07\x6d\x0d\x0a", 9); |
1847 | 0 | break; |
1848 | 0 | case 40: |
1849 | | // Send MID 0x6a/0d, to get back MID 0x6a/85 |
1850 | 0 | (void)sky_write(session, |
1851 | 0 | "\xA0\xA1\x00\x02\x6a\x0d\x67\x0d\x0a", 9); |
1852 | 0 | break; |
1853 | 0 | case 41: |
1854 | | // Send MID 0x6a/14, to get back MID 0x6a/86 |
1855 | 0 | (void)sky_write(session, |
1856 | 0 | "\xA0\xA1\x00\x02\x6a\x14\xfd\x0d\x0a", 9); |
1857 | 0 | break; |
1858 | 0 | case 42: |
1859 | | // Send MID 0x6a/16, to get back MID 0x6a/89 |
1860 | | // not on PX1172RH_DS ? |
1861 | 0 | (void)sky_write(session, |
1862 | 0 | "\xA0\xA1\x00\x02\x6a\x16\x7c\x0d\x0a", 9); |
1863 | 0 | break; |
1864 | 0 | case 43: |
1865 | | // Send MID 0x7a/0e/01, to get back MID 0x7a/0e/80 |
1866 | | // not on PX1172RH_DS ? |
1867 | 0 | (void)sky_write(session, |
1868 | 0 | "\xA0\xA1\x00\x03\x7a\x0e\x01\x75\x0d\x0a", 10); |
1869 | 0 | break; |
1870 | 0 | case 44: |
1871 | | // Send MID 0x7a/0e/02, to get back MID 0x7a/0e/81 |
1872 | | // not on PX1172RH_DS ? |
1873 | 0 | (void)sky_write(session, |
1874 | 0 | "\xA0\xA1\x00\x03\x7a\x0e\x02\x76\x0d\x0a", 10); |
1875 | 0 | break; |
1876 | 0 | case 45: |
1877 | | // Send MID 0x7a/0e/03, to get back MID 0x7a/0e/82 |
1878 | | // not on PX1172RH_DS ? |
1879 | 0 | (void)sky_write(session, |
1880 | 0 | "\xA0\xA1\x00\x03\x7a\x0e\x03\x77\x0d\x0a", 10); |
1881 | 0 | break; |
1882 | 0 | case 46: |
1883 | | // Send MID 0x7a/0e/05, to get back MID 0x7a/0e/83 |
1884 | | // not on PX1172RH_DS ? |
1885 | 0 | (void)sky_write(session, |
1886 | 0 | "\xA0\xA1\x00\x03\x7a\x0e\x05\x71\x0d\x0a", 10); |
1887 | 0 | break; |
1888 | 0 | default: |
1889 | | // Done |
1890 | 0 | session->cfg_stage = UINT_MAX; |
1891 | 0 | break; |
1892 | 0 | } |
1893 | 0 | } |
1894 | | |
1895 | 0 | if (SKY_PACKET == session->lexer.type) { |
1896 | 0 | return sky_parse(session, session->lexer.outbuffer, |
1897 | 0 | session->lexer.outbuflen); |
1898 | 0 | } |
1899 | 0 | if (NMEA_PACKET == session->lexer.type) { |
1900 | 0 | return nmea_parse((char *)session->lexer.outbuffer, session); |
1901 | 0 | } |
1902 | | // should not get here... |
1903 | | |
1904 | 0 | return 0; |
1905 | 0 | } |
1906 | | |
1907 | | // this is everything we export |
1908 | | // *INDENT-OFF* |
1909 | | const struct gps_type_t driver_skytraq = |
1910 | | { |
1911 | | .channels = SKY_CHANNELS, // consumer-grade GPS |
1912 | | .control_send = sky_write, // how to send a control string |
1913 | | .event_hook = NULL, // lifetime event handler |
1914 | | .flags = DRIVER_STICKY, // remember this |
1915 | | .get_packet = packet_get1, // be prepared for Skytraq or NMEA |
1916 | | .init_query = NULL, // non-perturbing initial qury |
1917 | | .mode_switcher = sky_mode, // Mode switcher |
1918 | | .packet_type = SKY_PACKET, // associated lexer packet type |
1919 | | .parse_packet = skybin_parse_input, // parse message packets |
1920 | | .probe_detect = NULL, // no probe |
1921 | | .rtcm_writer = gpsd_write, // send RTCM data straight |
1922 | | .trigger = NULL, // no trigger |
1923 | | .type_name = "Skytraq", // full name of type |
1924 | | }; |
1925 | | // *INDENT-ON* |
1926 | | #endif // defined SKYTRAQ_ENABLE) && defined(BINARY_ENABLE) |
1927 | | |
1928 | | // vim: set expandtab shiftwidth=4 |