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