1 /*
2 * Driver for the iTalk binary protocol used by FasTrax
3 *
4 * Week counters are not limited to 10 bits. It's unknown what
5 * the firmware is doing to disambiguate them, if anything; it might just
6 * be adding a fixed offset based on a hidden epoch value, in which case
7 * unhappy things will occur on the next rollover.
8 *
9 * This file is Copyright (c) 2010-2018 by the GPSD project
10 * SPDX-License-Identifier: BSD-2-clause
11 *
12 */
13
14 #include "gpsd_config.h" /* must be before all includes */
15
16 #include <stdio.h>
17 #include <stdbool.h>
18 #include <string.h>
19 #include <math.h>
20 #include <unistd.h>
21
22 #include "gpsd.h"
23 #if defined(ITRAX_ENABLE) && defined(BINARY_ENABLE)
24
25 #include "bits.h"
26 #include "driver_italk.h"
27 #include "timespec.h"
28
29 static gps_mask_t italk_parse(struct gps_device_t *, unsigned char *, size_t);
30 static gps_mask_t decode_itk_navfix(struct gps_device_t *, unsigned char *,
31 size_t);
32 static gps_mask_t decode_itk_prnstatus(struct gps_device_t *, unsigned char *,
33 size_t);
34 static gps_mask_t decode_itk_utcionomodel(struct gps_device_t *,
35 unsigned char *, size_t);
36 static gps_mask_t decode_itk_subframe(struct gps_device_t *, unsigned char *,
37 size_t);
38
39 /* NAVIGATION_MSG, message id 7 */
decode_itk_navfix(struct gps_device_t * session,unsigned char * buf,size_t len)40 static gps_mask_t decode_itk_navfix(struct gps_device_t *session,
41 unsigned char *buf, size_t len)
42 {
43 unsigned short flags, pflags;
44 timespec_t ts_tow;
45 uint32_t tow; /* Time of week [ms] */
46 char ts_buf[TIMESPEC_LEN];
47
48 gps_mask_t mask = 0;
49 if (len != 296) {
50 GPSD_LOG(LOG_PROG, &session->context->errout,
51 "ITALK: bad NAV_FIX (len %zu, should be 296)\n",
52 len);
53 return -1;
54 }
55
56 flags = (unsigned short) getleu16(buf, 7 + 4);
57 //cflags = (unsigned short) getleu16(buf, 7 + 6);
58 pflags = (unsigned short) getleu16(buf, 7 + 8);
59
60 session->gpsdata.status = STATUS_NO_FIX;
61 session->newdata.mode = MODE_NO_FIX;
62 mask = ONLINE_SET | MODE_SET | STATUS_SET | CLEAR_IS;
63
64 /* just bail out if this fix is not marked valid */
65 if (0 != (pflags & FIX_FLAG_MASK_INVALID)
66 || 0 == (flags & FIXINFO_FLAG_VALID))
67 return mask;
68
69 tow = getleu32(buf, 7 + 84); /* tow in ms */
70 MSTOTS(&ts_tow, tow);
71 session->newdata.time = gpsd_gpstime_resolv(session,
72 (unsigned short) getles16(buf, 7 + 82), ts_tow);
73 mask |= TIME_SET | NTPTIME_IS;
74
75 session->newdata.ecef.x = (double)(getles32(buf, 7 + 96) / 100.0);
76 session->newdata.ecef.y = (double)(getles32(buf, 7 + 100) / 100.0);
77 session->newdata.ecef.z = (double)(getles32(buf, 7 + 104) / 100.0);
78 session->newdata.ecef.vx = (double)(getles32(buf, 7 + 186) / 1000.0);
79 session->newdata.ecef.vy = (double)(getles32(buf, 7 + 190) / 1000.0);
80 session->newdata.ecef.vz = (double)(getles32(buf, 7 + 194) / 1000.0);
81 mask |= ECEF_SET | VECEF_SET;
82 /* this eph does not look right, badly documented.
83 * let gpsd_error_model() handle it
84 * session->newdata.eph = (double)(getles32(buf, 7 + 252) / 100.0);
85 */
86 session->newdata.eps = (double)(getles32(buf, 7 + 254) / 100.0);
87 /* compute epx/epy in gpsd_error_model(), not here */
88 mask |= HERR_SET;
89
90 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
91 session->gpsdata.satellites_used =
92 (int)MAX(getleu16(buf, 7 + 12), getleu16(buf, 7 + 14));
93 mask |= USED_IS;
94
95 if (flags & FIX_CONV_DOP_VALID) {
96 session->gpsdata.dop.hdop = (double)(getleu16(buf, 7 + 56) / 100.0);
97 session->gpsdata.dop.gdop = (double)(getleu16(buf, 7 + 58) / 100.0);
98 session->gpsdata.dop.pdop = (double)(getleu16(buf, 7 + 60) / 100.0);
99 session->gpsdata.dop.vdop = (double)(getleu16(buf, 7 + 62) / 100.0);
100 session->gpsdata.dop.tdop = (double)(getleu16(buf, 7 + 64) / 100.0);
101 mask |= DOP_SET;
102 }
103
104 if ((pflags & FIX_FLAG_MASK_INVALID) == 0
105 && (flags & FIXINFO_FLAG_VALID) != 0) {
106 if (pflags & FIX_FLAG_3DFIX)
107 session->newdata.mode = MODE_3D;
108 else
109 session->newdata.mode = MODE_2D;
110
111 if (pflags & FIX_FLAG_DGPS_CORRECTION)
112 session->gpsdata.status = STATUS_DGPS_FIX;
113 else
114 session->gpsdata.status = STATUS_FIX;
115 }
116
117 GPSD_LOG(LOG_DATA, &session->context->errout,
118 "NAV_FIX: time=%s, ecef x:%.2f y:%.2f z:%.2f altHAE=%.2f "
119 "speed=%.2f track=%.2f climb=%.2f mode=%d status=%d gdop=%.2f "
120 "pdop=%.2f hdop=%.2f vdop=%.2f tdop=%.2f\n",
121 timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
122 session->newdata.ecef.x,
123 session->newdata.ecef.y, session->newdata.ecef.z,
124 session->newdata.altHAE, session->newdata.speed,
125 session->newdata.track, session->newdata.climb,
126 session->newdata.mode, session->gpsdata.status,
127 session->gpsdata.dop.gdop, session->gpsdata.dop.pdop,
128 session->gpsdata.dop.hdop, session->gpsdata.dop.vdop,
129 session->gpsdata.dop.tdop);
130 return mask;
131 }
132
decode_itk_prnstatus(struct gps_device_t * session,unsigned char * buf,size_t len)133 static gps_mask_t decode_itk_prnstatus(struct gps_device_t *session,
134 unsigned char *buf, size_t len)
135 {
136 gps_mask_t mask;
137
138 if (len < 62) {
139 GPSD_LOG(LOG_PROG, &session->context->errout,
140 "ITALK: runt PRN_STATUS (len=%zu)\n", len);
141 mask = 0;
142 } else {
143 unsigned int i, nsv, nchan, st;
144 uint32_t msec = getleu32(buf, 7 + 6);
145 timespec_t ts_tow;
146 char ts_buf[TIMESPEC_LEN];
147
148 MSTOTS(&ts_tow, msec);
149
150 session->gpsdata.skyview_time = gpsd_gpstime_resolv(session,
151 (unsigned short)getleu16(buf, 7 + 4), ts_tow);
152 gpsd_zero_satellites(&session->gpsdata);
153 nchan = (unsigned int)getleu16(buf, 7 + 50);
154 if (nchan > MAX_NR_VISIBLE_PRNS)
155 nchan = MAX_NR_VISIBLE_PRNS;
156 for (i = st = nsv = 0; i < nchan; i++) {
157 unsigned int off = 7 + 52 + 10 * i;
158 unsigned short flags;
159 bool used;
160
161 flags = (unsigned short) getleu16(buf, off);
162 used = (bool)(flags & PRN_FLAG_USE_IN_NAV);
163 session->gpsdata.skyview[st].PRN = (short)(getleu16(buf, off + 4) & 0xff);
164 session->gpsdata.skyview[st].elevation =
165 (double)(getles16(buf, off + 6) & 0xff);
166 session->gpsdata.skyview[st].azimuth =
167 (double)(getles16(buf, off + 8) & 0xff);
168 session->gpsdata.skyview[st].ss =
169 (double)(getleu16(buf, off + 2) & 0xff);
170 session->gpsdata.skyview[st].used = used;
171 if (session->gpsdata.skyview[st].PRN > 0) {
172 st++;
173 if (used)
174 nsv++;
175 }
176 }
177 session->gpsdata.satellites_visible = (int)st;
178 session->gpsdata.satellites_used = (int)nsv;
179 mask = USED_IS | SATELLITE_SET;;
180
181 GPSD_LOG(LOG_DATA, &session->context->errout,
182 "PRN_STATUS: time=%s visible=%d used=%d "
183 "mask={USED|SATELLITE}\n",
184 timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
185 session->gpsdata.satellites_visible,
186 session->gpsdata.satellites_used);
187 }
188
189 return mask;
190 }
191
decode_itk_utcionomodel(struct gps_device_t * session,unsigned char * buf,size_t len)192 static gps_mask_t decode_itk_utcionomodel(struct gps_device_t *session,
193 unsigned char *buf, size_t len)
194 {
195 int leap;
196 unsigned short flags;
197 timespec_t ts_tow;
198 uint32_t tow; /* Time of week [ms] */
199 char ts_buf[TIMESPEC_LEN];
200
201 if (len != 64) {
202 GPSD_LOG(LOG_PROG, &session->context->errout,
203 "ITALK: bad UTC_IONO_MODEL (len %zu, should be 64)\n",
204 len);
205 return 0;
206 }
207
208 flags = (unsigned short) getleu16(buf, 7);
209 if (0 == (flags & UTC_IONO_MODEL_UTCVALID))
210 return 0;
211
212 leap = (int)getleu16(buf, 7 + 24);
213 if (session->context->leap_seconds < leap)
214 session->context->leap_seconds = leap;
215
216 tow = getleu32(buf, 7 + 38); /* in ms */
217 MSTOTS(&ts_tow, tow);
218 session->newdata.time = gpsd_gpstime_resolv(session,
219 (unsigned short) getleu16(buf, 7 + 36), ts_tow);
220 GPSD_LOG(LOG_DATA, &session->context->errout,
221 "UTC_IONO_MODEL: time=%s mask={TIME}\n",
222 timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)));
223 return TIME_SET | NTPTIME_IS;
224 }
225
decode_itk_subframe(struct gps_device_t * session,unsigned char * buf,size_t len)226 static gps_mask_t decode_itk_subframe(struct gps_device_t *session,
227 unsigned char *buf, size_t len)
228 {
229 unsigned short flags, prn, sf;
230 unsigned int i;
231 uint32_t words[10];
232
233 if (len != 64) {
234 GPSD_LOG(LOG_PROG, &session->context->errout,
235 "ITALK: bad SUBFRAME (len %zu, should be 64)\n", len);
236 return 0;
237 }
238
239 flags = (unsigned short) getleu16(buf, 7 + 4);
240 prn = (unsigned short) getleu16(buf, 7 + 6);
241 sf = (unsigned short) getleu16(buf, 7 + 8);
242 GPSD_LOG(LOG_PROG, &session->context->errout,
243 "iTalk 50B SUBFRAME prn %u sf %u - decode %s %s\n",
244 prn, sf,
245 (flags & SUBFRAME_WORD_FLAG_MASK) ? "error" : "ok",
246 (flags & SUBFRAME_GPS_PREAMBLE_INVERTED) ? "(inverted)" : "");
247 if (flags & SUBFRAME_WORD_FLAG_MASK)
248 return 0; // don't try decode an erroneous packet
249
250 /*
251 * Timo says "SUBRAME message contains decoded navigation message subframe
252 * words with parity checking done but parity bits still present."
253 */
254 for (i = 0; i < 10; i++)
255 words[i] = (uint32_t)(getleu32(buf, 7 + 14 + 4 * i) >> 6) & 0xffffff;
256
257 return gpsd_interpret_subframe(session, prn, words);
258 }
259
decode_itk_pseudo(struct gps_device_t * session,unsigned char * buf,size_t len)260 static gps_mask_t decode_itk_pseudo(struct gps_device_t *session,
261 unsigned char *buf, size_t len)
262 {
263 unsigned short flags, n, i;
264 unsigned int tow; /* time of week, in ms */
265 timespec_t ts_tow;
266
267 n = (unsigned short) getleu16(buf, 7 + 4);
268 if ((n < 1) || (n > MAXCHANNELS)){
269 GPSD_LOG(LOG_INF, &session->context->errout,
270 "ITALK: bad PSEUDO channel count\n");
271 return 0;
272 }
273
274 if (len != (size_t)((n+1)*36)) {
275 GPSD_LOG(LOG_PROG, &session->context->errout,
276 "ITALK: bad PSEUDO len %zu\n", len);
277 }
278
279 GPSD_LOG(LOG_PROG, &session->context->errout, "iTalk PSEUDO [%u]\n", n);
280 flags = (unsigned short)getleu16(buf, 7 + 6);
281 if ((flags & 0x3) != 0x3)
282 return 0; // bail if measurement time not valid.
283
284 tow = (unsigned int)getleu32(buf, 7 + 38);
285 MSTOTS(&ts_tow, tow);
286 session->newdata.time = gpsd_gpstime_resolv(session,
287 (unsigned short int)getleu16((char *)buf, 7 + 8), ts_tow);
288
289 session->gpsdata.raw.mtime = session->newdata.time;
290
291 /* this is so we can tell which never got set */
292 for (i = 0; i < MAXCHANNELS; i++)
293 session->gpsdata.raw.meas[i].svid = 0;
294 for (i = 0; i < n; i++){
295 session->gpsdata.skyview[i].PRN =
296 getleu16(buf, 7 + 26 + (i*36)) & 0xff;
297 session->gpsdata.skyview[i].ss =
298 getleu16(buf, 7 + 26 + (i*36 + 2)) & 0x3f;
299 session->gpsdata.raw.meas[i].satstat =
300 getleu32(buf, 7 + 26 + (i*36 + 4));
301 session->gpsdata.raw.meas[i].pseudorange =
302 getled64((char *)buf, 7 + 26 + (i*36 + 8));
303 session->gpsdata.raw.meas[i].doppler =
304 getled64((char *)buf, 7 + 26 + (i*36 + 16));
305 session->gpsdata.raw.meas[i].carrierphase =
306 getleu16(buf, 7 + 26 + (i*36 + 28));
307
308 session->gpsdata.raw.meas[i].codephase = NAN;
309 session->gpsdata.raw.meas[i].deltarange = NAN;
310 }
311 /* return RAW_IS; The above decode does not give reasonable results */
312 return 0; /* do not report valid until decode is fixed */
313 }
314
italk_parse(struct gps_device_t * session,unsigned char * buf,size_t len)315 static gps_mask_t italk_parse(struct gps_device_t *session,
316 unsigned char *buf, size_t len)
317 {
318 unsigned int type;
319 gps_mask_t mask = 0;
320
321 if (len == 0)
322 return 0;
323
324 type = (unsigned int) getub(buf, 4);
325 /* we may need to dump the raw packet */
326 GPSD_LOG(LOG_RAW, &session->context->errout,
327 "raw italk packet type 0x%02x\n", type);
328
329 session->cycle_end_reliable = true;
330
331 switch (type) {
332 case ITALK_NAV_FIX:
333 GPSD_LOG(LOG_DATA, &session->context->errout,
334 "iTalk NAV_FIX len %zu\n", len);
335 mask = decode_itk_navfix(session, buf, len) | (CLEAR_IS | REPORT_IS);
336 break;
337 case ITALK_PRN_STATUS:
338 GPSD_LOG(LOG_DATA, &session->context->errout,
339 "iTalk PRN_STATUS len %zu\n", len);
340 mask = decode_itk_prnstatus(session, buf, len);
341 break;
342 case ITALK_UTC_IONO_MODEL:
343 GPSD_LOG(LOG_DATA, &session->context->errout,
344 "iTalk UTC_IONO_MODEL len %zu\n", len);
345 mask = decode_itk_utcionomodel(session, buf, len);
346 break;
347
348 case ITALK_ACQ_DATA:
349 GPSD_LOG(LOG_DATA, &session->context->errout,
350 "iTalk ACQ_DATA len %zu\n", len);
351 break;
352 case ITALK_TRACK:
353 GPSD_LOG(LOG_DATA, &session->context->errout,
354 "iTalk TRACK len %zu\n", len);
355 break;
356 case ITALK_PSEUDO:
357 GPSD_LOG(LOG_DATA, &session->context->errout,
358 "iTalk PSEUDO len %zu\n", len);
359 mask = decode_itk_pseudo(session, buf, len);
360 break;
361 case ITALK_RAW_ALMANAC:
362 GPSD_LOG(LOG_DATA, &session->context->errout,
363 "iTalk RAW_ALMANAC len %zu\n", len);
364 break;
365 case ITALK_RAW_EPHEMERIS:
366 GPSD_LOG(LOG_DATA, &session->context->errout,
367 "iTalk RAW_EPHEMERIS len %zu\n", len);
368 break;
369 case ITALK_SUBFRAME:
370 mask = decode_itk_subframe(session, buf, len);
371 break;
372 case ITALK_BIT_STREAM:
373 GPSD_LOG(LOG_DATA, &session->context->errout,
374 "iTalk BIT_STREAM len %zu\n", len);
375 break;
376
377 case ITALK_AGC:
378 case ITALK_SV_HEALTH:
379 case ITALK_PRN_PRED:
380 case ITALK_FREQ_PRED:
381 case ITALK_DBGTRACE:
382 case ITALK_START:
383 case ITALK_STOP:
384 case ITALK_SLEEP:
385 case ITALK_STATUS:
386 case ITALK_ITALK_CONF:
387 case ITALK_SYSINFO:
388 case ITALK_ITALK_TASK_ROUTE:
389 case ITALK_PARAM_CTRL:
390 case ITALK_PARAMS_CHANGED:
391 case ITALK_START_COMPLETED:
392 case ITALK_STOP_COMPLETED:
393 case ITALK_LOG_CMD:
394 case ITALK_SYSTEM_START:
395 case ITALK_STOP_SEARCH:
396 case ITALK_SEARCH:
397 case ITALK_PRED_SEARCH:
398 case ITALK_SEARCH_DONE:
399 case ITALK_TRACK_DROP:
400 case ITALK_TRACK_STATUS:
401 case ITALK_HANDOVER_DATA:
402 case ITALK_CORE_SYNC:
403 case ITALK_WAAS_RAWDATA:
404 case ITALK_ASSISTANCE:
405 case ITALK_PULL_FIX:
406 case ITALK_MEMCTRL:
407 case ITALK_STOP_TASK:
408 GPSD_LOG(LOG_DATA, &session->context->errout,
409 "iTalk not processing packet: id 0x%02x length %zu\n",
410 type, len);
411 break;
412 default:
413 GPSD_LOG(LOG_DATA, &session->context->errout,
414 "iTalk unknown packet: id 0x%02x length %zu\n",
415 type, len);
416 }
417
418 return mask | ONLINE_SET;
419 }
420
421
italk_parse_input(struct gps_device_t * session)422 static gps_mask_t italk_parse_input(struct gps_device_t *session)
423 {
424 if (session->lexer.type == ITALK_PACKET) {
425 return italk_parse(session, session->lexer.outbuffer,
426 session->lexer.outbuflen);;
427 #ifdef NMEA0183_ENABLE
428 } else if (session->lexer.type == NMEA_PACKET) {
429 return nmea_parse((char *)session->lexer.outbuffer, session);
430 #endif /* NMEA0183_ENABLE */
431 } else
432 return 0;
433 }
434
435 #ifdef __UNUSED__
italk_ping(struct gps_device_t * session)436 static void italk_ping(struct gps_device_t *session)
437 /* send a "ping". it may help us detect an itrax more quickly */
438 {
439 char *ping = "<?>";
440 (void)gpsd_write(session, ping, 3);
441 }
442 #endif /* __UNUSED__ */
443
444 /* *INDENT-OFF* */
445 const struct gps_type_t driver_italk =
446 {
447 .type_name = "iTalk", /* full name of type */
448 .packet_type = ITALK_PACKET, /* associated lexer packet type */
449 .flags = DRIVER_STICKY, /* no rollover or other flags */
450 .trigger = NULL, /* recognize the type */
451 .channels = 12, /* consumer-grade GPS */
452 .probe_detect = NULL, /* how to detect at startup time */
453 .get_packet = generic_get, /* use generic packet grabber */
454 .parse_packet = italk_parse_input,/* parse message packets */
455 .rtcm_writer = gpsd_write, /* send RTCM data straight */
456 .init_query = NULL, /* non-perturbing initial query */
457 .event_hook = NULL, /* lifetime event handler */
458 #ifdef RECONFIGURE_ENABLE
459 .speed_switcher = NULL, /* no speed switcher */
460 .mode_switcher = NULL, /* no mode switcher */
461 .rate_switcher = NULL, /* no sample-rate switcher */
462 .min_cycle.tv_sec = 1, /* not relevant, no rate switch */
463 .min_cycle.tv_nsec = 0, /* not relevant, no rate switch */
464 #endif /* RECONFIGURE_ENABLE */
465 #ifdef CONTROLSEND_ENABLE
466 .control_send = NULL, /* no control string sender */
467 #endif /* CONTROLSEND_ENABLE */
468 .time_offset = NULL, /* no method for NTP fudge factor */
469 };
470 /* *INDENT-ON* */
471 #endif /* defined(ITRAX_ENABLE) && defined(BINARY_ENABLE) */
472