cleanup specfile for packaging
[profile/ivi/gpsd.git] / driver_tsip.c
1 /*
2  * Handle the Trimble TSIP packet format
3  * by Rob Janssen, PE1CHL.
4  *
5  * This file is Copyright (c) 2010 by the GPSD project
6  * BSD terms apply: see the file COPYING in the distribution root for details.
7  */
8 #include <sys/types.h>
9 #include <stdio.h>
10 #include <string.h>
11 #ifndef S_SPLINT_S
12 #include <unistd.h>
13 #endif /* S_SPLINT_S */
14 #include <math.h>
15 #include "gpsd_config.h"
16
17 #if defined (HAVE_SYS_SELECT_H)
18 #include <sys/select.h>
19 #endif
20 #if defined(HAVE_SYS_TIME_H)
21 #include <sys/time.h>
22 #endif
23
24 #include "gpsd.h"
25 #include "bits.h"
26
27 #define USE_SUPERPACKET 1       /* use Super Packet mode? */
28
29 #define SEMI_2_DEG      (180.0 / 2147483647)    /* 2^-31 semicircle to deg */
30
31 #ifdef TSIP_ENABLE
32 #define TSIP_CHANNELS   12
33
34 static int tsip_write(struct gps_device_t *session,
35                       unsigned int id, /*@null@*/ unsigned char *buf,
36                       size_t len)
37 {
38     char *ep, *cp;
39
40     gpsd_report(LOG_IO, "Sent TSIP packet id 0x%02x: %s\n", id,
41                 gpsd_hexdump_wrapper(buf, len, LOG_IO));
42
43     /*@ +charint @*/
44     session->msgbuf[0] = '\x10';
45     session->msgbuf[1] = (char)id;
46     ep = session->msgbuf + 2;
47     /*@ -nullderef @*/
48     for (cp = (char *)buf; len-- > 0; cp++) {
49         if (*cp == '\x10')
50             *ep++ = '\x10';
51         *ep++ = *cp;
52     }
53     /*@ +nullderef @*/
54     *ep++ = '\x10';
55     *ep++ = '\x03';
56     session->msgbuflen = (size_t) (ep - session->msgbuf);
57     /*@ -charint @*/
58     if (gpsd_write(session, session->msgbuf, session->msgbuflen) !=
59         (ssize_t) session->msgbuflen)
60         return -1;
61
62     return 0;
63 }
64
65 /* tsip_detect()
66  *
67  * see if it looks like a TSIP device (speaking 9600O81) is listening and
68  * return 1 if found, 0 if not
69  */
70 static bool tsip_detect(struct gps_device_t *session)
71 {
72     char buf[BUFSIZ];
73     unsigned int n;
74     bool ret = false;
75     int myfd;
76     fd_set fdset;
77     struct timeval to;
78     speed_t old_baudrate;
79     char old_parity;
80     unsigned int old_stopbits;
81
82     gpsd_report(LOG_PROG, "Probing TSIP\n");
83
84     old_baudrate = session->gpsdata.dev.baudrate;
85     old_parity = session->gpsdata.dev.parity;
86     old_stopbits = session->gpsdata.dev.stopbits;
87     gpsd_set_speed(session, 9600, 'O', 1);
88
89     /* request firmware revision and look for a valid response */
90     /*@+ignoresigns@*/
91     putbyte(buf, 0, 0x10);
92     putbyte(buf, 1, 0x1f);
93     putbyte(buf, 2, 0x10);
94     putbyte(buf, 3, 0x03);
95     /*@+ignoresigns@*/
96     myfd = session->gpsdata.gps_fd;
97     if (write(myfd, buf, 4) == 4) {
98         for (n = 0; n < 3; n++) {
99             FD_ZERO(&fdset);
100             FD_SET(myfd, &fdset);
101             to.tv_sec = 1;
102             to.tv_usec = 0;
103             if (select(myfd + 1, &fdset, NULL, NULL, &to) != 1)
104                 break;
105             if (generic_get(session) >= 0) {
106                 if (session->packet.type == TSIP_PACKET) {
107                     gpsd_report(LOG_RAW, "tsip_detect found\n");
108                     ret = true;
109                     break;
110                 }
111             }
112         }
113     }
114
115     if (!ret)
116         /* return serial port to original settings */
117         gpsd_set_speed(session, old_baudrate, old_parity, old_stopbits);
118
119     return ret;
120 }
121
122 static gps_mask_t tsip_analyze(struct gps_device_t *session)
123 {
124     int i, j, len, count;
125     gps_mask_t mask = 0;
126     unsigned int id;
127     uint8_t u1, u2, u3, u4, u5;
128     int16_t s1, s2, s3, s4;
129     int32_t sl1, sl2, sl3;
130     uint32_t ul1, ul2;
131     float f1, f2, f3, f4, f5;
132     double d1, d2, d3, d4, d5;
133     union int_float i_f;
134     union long_double l_d;
135     time_t now;
136     unsigned char buf[BUFSIZ];
137     char buf2[BUFSIZ];
138
139     if (session->packet.type != TSIP_PACKET) {
140         gpsd_report(LOG_INF, "tsip_analyze packet type %d\n",
141                     session->packet.type);
142         return 0;
143     }
144
145     /*@ +charint @*/
146     if (session->packet.outbuflen < 4 || session->packet.outbuffer[0] != 0x10)
147         return 0;
148
149     /* remove DLE stuffing and put data part of message in buf */
150
151     memset(buf, 0, sizeof(buf));
152     buf2[len = 0] = '\0';
153     for (i = 2; i < (int)session->packet.outbuflen; i++) {
154         if (session->packet.outbuffer[i] == 0x10)
155             if (session->packet.outbuffer[++i] == 0x03)
156                 break;
157
158         (void)snprintf(buf2 + strlen(buf2),
159                        sizeof(buf2) - strlen(buf2),
160                        "%02x", buf[len++] = session->packet.outbuffer[i]);
161     }
162     /*@ -charint @*/
163
164     (void)snprintf(session->gpsdata.tag, sizeof(session->gpsdata.tag),
165                    "ID%02x", id = (unsigned)session->packet.outbuffer[1]);
166
167     gpsd_report(LOG_IO, "TSIP packet id 0x%02x length %d: %s\n", id, len,
168                 buf2);
169     (void)time(&now);
170
171     session->cycle_end_reliable = true;
172     switch (id) {
173     case 0x13:                  /* Packet Received */
174         u1 = getub(buf, 0);
175         u2 = getub(buf, 1);
176         gpsd_report(LOG_WARN,
177                     "Received packet of type %02x cannot be parsed\n", u1);
178 #if USE_SUPERPACKET
179         if ((int)u1 == 0x8e && (int)u2 == 0x23) {       /* no Compact Super Packet */
180             gpsd_report(LOG_WARN, "No Compact Super Packet, use LFwEI\n");
181
182             /* Request LFwEI Super Packet */
183             putbyte(buf, 0, 0x20);
184             putbyte(buf, 1, 0x01);      /* enabled */
185             (void)tsip_write(session, 0x8e, buf, 2);
186         }
187 #endif /* USE_SUPERPACKET */
188         break;
189     case 0x41:                  /* GPS Time */
190         if (len != 10)
191             break;
192         session->driver.tsip.last_41 = now;     /* keep timestamp for request */
193         f1 = getbef(buf, 0);    /* gpstime */
194         s1 = getbesw(buf, 4);   /* week */
195         f2 = getbef(buf, 6);    /* leap seconds */
196         if (f1 >= 0.0 && f2 > 10.0) {
197             session->context->gps_week = s1;
198             session->context->leap_seconds = (int)round(f2);
199             session->context->valid |= LEAP_SECOND_VALID;
200             session->context->gps_tow = f1;
201
202             session->newdata.time =
203                 gpstime_to_unix(session->context->gps_week,
204                                 session->context->gps_tow) -
205                 session->context->leap_seconds;
206             mask |= TIME_IS;
207         }
208         gpsd_report(LOG_INF, "GPS Time %f %d %f\n", f1, s1, f2);
209         break;
210     case 0x42:                  /* Single-Precision Position Fix, XYZ ECEF */
211         if (len != 16)
212             break;
213         f1 = getbef(buf, 0);    /* X */
214         f2 = getbef(buf, 4);    /* Y */
215         f3 = getbef(buf, 8);    /* Z */
216         f4 = getbef(buf, 12);   /* time-of-fix */
217         gpsd_report(LOG_INF, "GPS Position XYZ %f %f %f %f\n", f1, f2, f3,
218                     f4);
219         break;
220     case 0x43:                  /* Velocity Fix, XYZ ECEF */
221         if (len != 20)
222             break;
223         f1 = getbef(buf, 0);    /* X velocity */
224         f2 = getbef(buf, 4);    /* Y velocity */
225         f3 = getbef(buf, 8);    /* Z velocity */
226         f4 = getbef(buf, 12);   /* bias rate */
227         f5 = getbef(buf, 16);   /* time-of-fix */
228         gpsd_report(LOG_INF, "GPS Velocity XYZ %f %f %f %f %f\n", f1, f2, f3,
229                     f4, f5);
230         break;
231     case 0x45:                  /* Software Version Information */
232         if (len != 10)
233             break;
234         /*@ -formattype @*/
235         (void)snprintf(session->subtype, sizeof(session->subtype),
236                        "%d.%d %02d%02d%02d %d.%d %02d%02d%02d",
237                        getub(buf, 0), getub(buf, 1), getub(buf, 4), getub(buf,
238                                                                           2),
239                        getub(buf, 3), getub(buf, 5), getub(buf, 6), getub(buf,
240                                                                           9),
241                        getub(buf, 7), getub(buf, 8));
242         /*@ +formattype @*/
243         gpsd_report(LOG_INF, "Software version: %s\n", session->subtype);
244         mask |= DEVICEID_IS;
245         break;
246     case 0x46:                  /* Health of Receiver */
247         if (len != 2)
248             break;
249         session->driver.tsip.last_46 = now;
250         u1 = getub(buf, 0);     /* Status code */
251         u2 = getub(buf, 1);     /* Antenna/Battery */
252         if (u1 != (uint8_t) 0) {
253             session->gpsdata.status = STATUS_NO_FIX;
254             mask |= STATUS_IS;
255         } else {
256             if (session->gpsdata.status < STATUS_FIX) {
257                 session->gpsdata.status = STATUS_FIX;
258                 mask |= STATUS_IS;
259             }
260         }
261         gpsd_report(LOG_PROG, "Receiver health %02x %02x\n", u1, u2);
262         break;
263     case 0x47:                  /* Signal Levels for all Satellites */
264         gpsd_zero_satellites(&session->gpsdata);
265         count = (int)getub(buf, 0);     /* satellite count */
266         if (len != (5 * count + 1))
267             break;
268         buf2[0] = '\0';
269         for (i = 0; i < count; i++) {
270             u1 = getub(buf, 5 * i + 1);
271             if ((f1 = getbef(buf, 5 * i + 2)) < 0)
272                 f1 = 0.0;
273             for (j = 0; j < TSIP_CHANNELS; j++)
274                 if (session->gpsdata.PRN[j] == (int)u1) {
275                     session->gpsdata.ss[j] = f1;
276                     break;
277                 }
278             (void)snprintf(buf2 + strlen(buf2), sizeof(buf2) - strlen(buf2),
279                            " %d=%.1f", (int)u1, f1);
280         }
281         gpsd_report(LOG_PROG, "Signal Levels (%d):%s\n", count, buf2);
282         mask |= SATELLITE_IS;
283         break;
284     case 0x48:                  /* GPS System Message */
285         buf[len] = '\0';
286         gpsd_report(LOG_PROG, "GPS System Message: %s\n", buf);
287         break;
288     case 0x49:                  /* Almanac Health Page */
289         break;
290     case 0x4a:                  /* Single-Precision Position LLA */
291         if (len != 20)
292             break;
293         session->newdata.latitude = getbef(buf, 0) * RAD_2_DEG;
294         session->newdata.longitude = getbef(buf, 4) * RAD_2_DEG;
295         session->newdata.altitude = getbef(buf, 8);
296         f1 = getbef(buf, 12);   /* clock bias */
297         f2 = getbef(buf, 16);   /* time-of-fix */
298         session->context->gps_tow = f2;
299         if (session->context->gps_week) {
300             session->newdata.time =
301                 gpstime_to_unix((int)session->context->gps_week,
302                                 session->context->gps_tow)
303                 - session->context->leap_seconds;
304             mask |= TIME_IS;
305         }
306         mask |= LATLON_IS | ALTITUDE_IS | CLEAR_IS | REPORT_IS;
307         gpsd_report(LOG_DATA, "SPPLLA 0x4a "
308                     "time=%.2f lat=%.2f lon=%.2f alt=%.2f mask=%s\n",
309                     session->newdata.time,
310                     session->newdata.latitude,
311                     session->newdata.longitude,
312                     session->newdata.altitude, gpsd_maskdump(mask));
313         break;
314     case 0x4b:                  /* Machine/Code ID and Additional Status */
315         if (len != 3)
316             break;
317         u1 = getub(buf, 0);     /* Machine ID */
318         u2 = getub(buf, 1);     /* Status 1 */
319         u3 = getub(buf, 2);     /* Status 2 */
320         gpsd_report(LOG_INF, "Machine ID %02x %02x %02x\n", u1, u2, u3);
321 #if USE_SUPERPACKET
322         if ((u3 & 0x01) != (uint8_t) 0 && !session->driver.tsip.superpkt) {
323             gpsd_report(LOG_PROG, "Switching to Super Packet mode\n");
324
325             /* set new I/O Options for Super Packet output */
326             putbyte(buf, 0, 0x2c);      /* Position: SP, MSL */
327             putbyte(buf, 1, 0x00);      /* Velocity: none (via SP) */
328             putbyte(buf, 2, 0x00);      /* Time: GPS */
329             putbyte(buf, 3, 0x08);      /* Aux: dBHz */
330             (void)tsip_write(session, 0x35, buf, 4);
331             session->driver.tsip.superpkt = true;
332         }
333 #endif /* USE_SUPERPACKET */
334         break;
335     case 0x4c:                  /* Operating Parameters Report */
336         break;
337     case 0x54:                  /* One Satellite Bias */
338         break;
339     case 0x55:                  /* IO Options */
340         if (len != 4)
341             break;
342         u1 = getub(buf, 0);     /* Position */
343         u2 = getub(buf, 1);     /* Velocity */
344         u3 = getub(buf, 2);     /* Timing */
345         u4 = getub(buf, 3);     /* Aux */
346         gpsd_report(LOG_INF, "IO Options %02x %02x %02x %02x\n", u1, u2, u3,
347                     u4);
348 #if USE_SUPERPACKET
349         if ((u1 & 0x20) != (uint8_t) 0) {       /* Output Super Packets? */
350             /* No LFwEI Super Packet */
351             putbyte(buf, 0, 0x20);
352             putbyte(buf, 1, 0x00);      /* disabled */
353             (void)tsip_write(session, 0x8e, buf, 2);
354
355             /* Request Compact Super Packet */
356             putbyte(buf, 0, 0x23);
357             putbyte(buf, 1, 0x01);      /* enabled */
358             (void)tsip_write(session, 0x8e, buf, 2);
359             session->driver.tsip.req_compact = now;
360         }
361 #endif /* USE_SUPERPACKET */
362         break;
363     case 0x56:                  /* Velocity Fix, East-North-Up (ENU) */
364         if (len != 20)
365             break;
366         f1 = getbef(buf, 0);    /* East velocity */
367         f2 = getbef(buf, 4);    /* North velocity */
368         f3 = getbef(buf, 8);    /* Up velocity */
369         f4 = getbef(buf, 12);   /* clock bias rate */
370         f5 = getbef(buf, 16);   /* time-of-fix */
371         session->newdata.climb = f3;
372         /*@ -evalorder @*/
373         session->newdata.speed = sqrt(pow(f2, 2) + pow(f1, 2));
374         /*@ +evalorder @*/
375         if ((session->newdata.track = atan2(f1, f2) * RAD_2_DEG) < 0)
376             session->newdata.track += 360.0;
377         gpsd_report(LOG_INF, "GPS Velocity ENU %f %f %f %f %f\n", f1, f2, f3,
378                     f4, f5);
379         mask |= SPEED_IS | TRACK_IS | CLIMB_IS;
380         gpsd_report(LOG_DATA, "VFENU 0x56 "
381                     "time=%.2f speed=%.2f track=%.2f climb=%.2f mask=%s\n",
382                     session->newdata.time,
383                     session->newdata.speed,
384                     session->newdata.track,
385                     session->newdata.climb, gpsd_maskdump(mask));
386         break;
387     case 0x57:                  /* Information About Last Computed Fix */
388         if (len != 8)
389             break;
390         u1 = getub(buf, 0);     /* Source of information */
391         u2 = getub(buf, 1);     /* Mfg. diagnostic */
392         f1 = getbef(buf, 2);    /* gps_time */
393         s1 = getbesw(buf, 6);   /* tsip.gps_week */
394         /*@ +charint @*/
395         if (getub(buf, 0) == 0x01)      /* good current fix? */
396             session->context->gps_week = s1;
397         /*@ -charint @*/
398         gpsd_report(LOG_INF, "Fix info %02x %02x %d %f\n", u1, u2, s1, f1);
399         break;
400     case 0x58:                  /* Satellite System Data/Acknowledge from Receiver */
401         break;
402     case 0x59:                  /* Status of Satellite Disable or Ignore Health */
403         break;
404     case 0x5a:                  /* Raw Measurement Data */
405         if (len != 29)
406             break;
407         f1 = getbef(buf, 5);    /* Signal Level */
408         f2 = getbef(buf, 9);    /* Code phase */
409         f3 = getbef(buf, 13);   /* Doppler */
410         d1 = getbed(buf, 17);   /* Time of Measurement */
411         gpsd_report(LOG_PROG, "Raw Measurement Data %d %f %f %f %f\n",
412                     getub(buf, 0), f1, f2, f3, d1);
413         break;
414     case 0x5b:                  /* Satellite Ephemeris Status */
415         break;
416     case 0x5c:                  /* Satellite Tracking Status */
417         if (len != 24)
418             break;
419         u1 = getub(buf, 0);     /* PRN */
420         u2 = getub(buf, 1);     /* chan */
421         u3 = getub(buf, 2);     /* Acquisition flag */
422         u4 = getub(buf, 3);     /* Ephemeris flag */
423         f1 = getbef(buf, 4);    /* Signal level */
424         f2 = getbef(buf, 8);    /* time of Last measurement */
425         d1 = getbef(buf, 12) * RAD_2_DEG;       /* Elevation */
426         d2 = getbef(buf, 16) * RAD_2_DEG;       /* Azimuth */
427         i = (int)(u2 >> 3);     /* channel number */
428         gpsd_report(LOG_INF,
429                     "Satellite Tracking Status: Ch %2d PRN %3d Res %d Acq %d Eph %2d SNR %4.1f LMT %.04f El %4.1f Az %5.1f\n",
430                     i, u1, u2 & 7, u3, u4, f1, f2, d1, d2);
431         if (i < TSIP_CHANNELS) {
432             if (d1 >= 0.0) {
433                 session->gpsdata.PRN[i] = (int)u1;
434                 session->gpsdata.ss[i] = f1;
435                 session->gpsdata.elevation[i] = (int)round(d1);
436                 session->gpsdata.azimuth[i] = (int)round(d2);
437             } else {
438                 session->gpsdata.PRN[i] = session->gpsdata.elevation[i]
439                     = session->gpsdata.azimuth[i] = 0;
440                 session->gpsdata.ss[i] = 0.0;
441             }
442             if (++i == session->gpsdata.satellites_visible) {
443                 session->gpsdata.skyview_time = NAN;
444                 mask |= SATELLITE_IS;   /* last of the series */
445             }
446             if (i > session->gpsdata.satellites_visible)
447                 session->gpsdata.satellites_visible = i;
448         }
449         break;
450     case 0x5e:                  /* Additional Fix Status Report */
451         break;
452     case 0x6d:                  /* All-In-View Satellite Selection */
453         u1 = getub(buf, 0);     /* nsvs/dimension */
454         count = (int)((u1 >> 4) & 0x0f);
455         if (len != (17 + count))
456             break;
457         session->driver.tsip.last_6d = now;     /* keep timestamp for request */
458 #ifdef __UNUSED__
459         /*
460          * This looks right, but it sets a spurious mode value when
461          * the satellite constellation looks good to the chip but no
462          * actual fix has yet been acquired.  We should set the mode
463          * field (which controls gpsd's fix reporting) only from sentences
464          * that convey actual fix information, like 0x20, othewise we
465          * get results like triggering ther error modeler spuriously.
466          */
467         switch (u1 & 7) {       /* dimension */
468         case 3:
469             //session->gpsdata.status = STATUS_FIX;
470             session->newdata.mode = MODE_2D;
471             break;
472         case 4:
473             //session->gpsdata.status = STATUS_FIX;
474             session->newdata.mode = MODE_3D;
475             break;
476         default:
477             //session->gpsdata.status = STATUS_NO_FIX;
478             session->newdata.mode = MODE_NO_FIX;
479             break;
480         }
481         mask |= MODE_IS;
482 #endif /* __UNUSED__ */
483         session->gpsdata.satellites_used = count;
484         session->gpsdata.dop.pdop = getbef(buf, 1);
485         session->gpsdata.dop.hdop = getbef(buf, 5);
486         session->gpsdata.dop.vdop = getbef(buf, 9);
487         session->gpsdata.dop.tdop = getbef(buf, 13);
488         /*@ -evalorder @*/
489         session->gpsdata.dop.gdop =
490             sqrt(pow(session->gpsdata.dop.pdop, 2) +
491                  pow(session->gpsdata.dop.tdop, 2));
492         /*@ +evalorder @*/
493
494         memset(session->gpsdata.used, 0, sizeof(session->gpsdata.used));
495         buf2[0] = '\0';
496         /*@ +charint @*/
497         for (i = 0; i < count; i++)
498             (void)snprintf(buf2 + strlen(buf2), sizeof(buf2) - strlen(buf2),
499                            " %d", session->gpsdata.used[i] =
500                            (int)getub(buf, 17 + i));
501         /*@ -charint @*/
502         gpsd_report(LOG_DATA, "AIVSS: 0x6d "
503                     "status=%d used=%d "
504                     "pdop=%.1f hdop=%.1f vdop=%.1f tdop=%.1f gdup=%.1f "
505                     "mask=%s\n",
506                     session->gpsdata.status,
507                     session->gpsdata.satellites_used,
508                     session->gpsdata.dop.pdop,
509                     session->gpsdata.dop.hdop,
510                     session->gpsdata.dop.vdop,
511                     session->gpsdata.dop.tdop,
512                     session->gpsdata.dop.gdop, gpsd_maskdump(mask));
513         mask |= DOP_IS | STATUS_IS | USED_IS;
514         break;
515     case 0x6e:                  /* Synchronized Measurements */
516         break;
517     case 0x6f:                  /* Synchronized Measurements Report */
518         /*@ +charint @*/
519         if (len < 20 || getub(buf, 0) != 1 || getub(buf, 1) != 2)
520             break;
521         /*@ -charint @*/
522         s1 = getbesw(buf, 2);   /* number of bytes */
523         u1 = getub(buf, 20);    /* number of SVs */
524         break;
525     case 0x70:                  /* Filter Report */
526         break;
527     case 0x7a:                  /* NMEA settings */
528         break;
529     case 0x82:                  /* Differential Position Fix Mode */
530         if (len != 1)
531             break;
532         u1 = getub(buf, 0);     /* fix mode */
533         /*@ +charint @*/
534         if (session->gpsdata.status == STATUS_FIX && (u1 & 0x01) != 0) {
535             session->gpsdata.status = STATUS_DGPS_FIX;
536             mask |= STATUS_IS;
537         }
538         /*@ -charint @*/
539         gpsd_report(LOG_DATA, "DPFM 0x82 status=%d mask=%s\n",
540                     session->gpsdata.status, gpsd_maskdump(mask));
541         break;
542     case 0x83:                  /* Double-Precision XYZ Position Fix and Bias Information */
543         if (len != 36)
544             break;
545         d1 = getbed(buf, 0);    /* X */
546         d2 = getbed(buf, 8);    /* Y */
547         d3 = getbed(buf, 16);   /* Z */
548         d4 = getbed(buf, 24);   /* clock bias */
549         f1 = getbef(buf, 32);   /* time-of-fix */
550         gpsd_report(LOG_INF, "GPS Position XYZ %f %f %f %f %f\n", d1, d2, d3,
551                     d4, f1);
552         break;
553     case 0x84:                  /* Double-Precision LLA Position Fix and Bias Information */
554         if (len != 36)
555             break;
556         session->newdata.latitude = getbed(buf, 0) * RAD_2_DEG;
557         session->newdata.longitude = getbed(buf, 8) * RAD_2_DEG;
558         session->newdata.altitude = getbed(buf, 16);
559         d1 = getbed(buf, 24);   /* clock bias */
560         f1 = getbef(buf, 32);   /* time-of-fix */
561         session->context->gps_tow = f1;
562         if (session->context->gps_week) {
563             session->newdata.time =
564                 gpstime_to_unix((int)session->context->gps_week,
565                                 session->context->gps_tow)
566                 - session->context->leap_seconds;
567             mask |= TIME_IS;
568         }
569         gpsd_report(LOG_INF, "GPS DP LLA %f %f %f %f\n",
570                     session->newdata.time,
571                     session->newdata.latitude,
572                     session->newdata.longitude, session->newdata.altitude);
573         mask |= LATLON_IS | ALTITUDE_IS | CLEAR_IS | REPORT_IS;
574         gpsd_report(LOG_DATA, "DPPLLA 0x84 "
575                     "time=%.2f lat=%.2f lon=%.2f alt=%.2f mask=%s\n",
576                     session->newdata.time,
577                     session->newdata.latitude,
578                     session->newdata.longitude,
579                     session->newdata.altitude, gpsd_maskdump(mask));
580         break;
581     case 0x8f:                  /* Super Packet.  Well...  */
582         /*@ +charint @*/
583         u1 = (uint8_t) getub(buf, 0);
584         (void)snprintf(session->gpsdata.tag + strlen(session->gpsdata.tag),
585                        sizeof(session->gpsdata.tag) -
586                        strlen(session->gpsdata.tag), "%02x", (uint) u1);
587         /*@ -charint @*/
588         switch (u1) {           /* sub-packet ID */
589         case 0x15:              /* Current Datum Values */
590             if (len != 43)
591                 break;
592             s1 = getbesw(buf, 1);       /* Datum Index */
593             d1 = getbed(buf, 3);        /* DX */
594             d2 = getbed(buf, 11);       /* DY */
595             d3 = getbed(buf, 19);       /* DZ */
596             d4 = getbed(buf, 27);       /* A-axis */
597             d5 = getbed(buf, 35);       /* Eccentricity Squared */
598             gpsd_report(LOG_INF, "Current Datum %d %f %f %f %f %f\n", s1, d1,
599                         d2, d3, d4, d5);
600             break;
601
602         case 0x20:              /* Last Fix with Extra Information (binary fixed point) */
603             /* CSK sez "why does my Lassen iQ output oversize packets?" */
604             if ((len != 56) && (len != 64))
605                 break;
606             s1 = getbesw(buf, 2);       /* east velocity */
607             s2 = getbesw(buf, 4);       /* north velocity */
608             s3 = getbesw(buf, 6);       /* up velocity */
609             ul1 = getbeul(buf, 8);      /* time */
610             sl1 = getbesl(buf, 12);     /* latitude */
611             ul2 = getbeul(buf, 16);     /* longitude */
612             sl2 = getbesl(buf, 20);     /* altitude */
613             u1 = getub(buf, 24);        /* velocity scaling */
614             u2 = getub(buf, 27);        /* fix flags */
615             u3 = getub(buf, 28);        /* num svs */
616             u4 = getub(buf, 29);        /* utc offset */
617             s4 = getbesw(buf, 30);      /* tsip.gps_week */
618             /* PRN/IODE data follows */
619             gpsd_report(LOG_RAW,
620                         "LFwEI %d %d %d %u %d %u %u %x %x %u %u %d\n", s1, s2,
621                         s3, ul1, sl1, ul2, sl2, u1, u2, u3, u4, s4);
622
623             if ((u1 & 0x01) != (uint8_t) 0)     /* check velocity scaling */
624                 d5 = 0.02;
625             else
626                 d5 = 0.005;
627             d1 = s1 * d5;       /* east velocity m/s */
628             d2 = s2 * d5;       /* north velocity m/s */
629             session->newdata.climb = s3 * d5;   /* up velocity m/s */
630             /*@ -evalorder @*/
631             session->newdata.speed = sqrt(pow(d2, 2) + pow(d1, 2));
632             /*@ +evalorder @*/
633             if ((session->newdata.track = atan2(d1, d2) * RAD_2_DEG) < 0)
634                 session->newdata.track += 360.0;
635             session->newdata.latitude = sl1 * SEMI_2_DEG;
636             /*@i1@*/ session->newdata.longitude = ul2 * SEMI_2_DEG;
637             if (session->newdata.longitude > 180.0)
638                 session->newdata.longitude -= 360.0;
639             session->gpsdata.separation =
640                 wgs84_separation(session->newdata.latitude,
641                                  session->newdata.longitude);
642             session->newdata.altitude =
643                 sl2 * 1e-3 - session->gpsdata.separation;;
644             session->gpsdata.status = STATUS_NO_FIX;
645             session->newdata.mode = MODE_NO_FIX;
646             if ((u2 & 0x01) == (uint8_t) 0) {   /* Fix Available */
647                 session->gpsdata.status = STATUS_FIX;
648                 if ((u2 & 0x02) != (uint8_t) 0) /* DGPS Corrected */
649                     session->gpsdata.status = STATUS_DGPS_FIX;
650                 if ((u2 & 0x04) != (uint8_t) 0) /* Fix Dimension */
651                     session->newdata.mode = MODE_2D;
652                 else
653                     session->newdata.mode = MODE_3D;
654             }
655             session->gpsdata.satellites_used = (int)u3;
656             if ((int)u4 > 10) {
657                 session->context->leap_seconds = (int)u4;
658                 session->context->valid |= LEAP_SECOND_VALID;
659             }
660             session->context->gps_week = (unsigned short)s4;
661             session->context->gps_tow = (double)ul1 *1e-3;
662             /*@ ignore @*//*@ splint is confused @ */
663             session->newdata.time =
664                 gpstime_to_unix((int)s4, session->context->gps_tow)
665                 - session->context->leap_seconds;
666             /*@ end @*/
667             mask |=
668                 TIME_IS | LATLON_IS | ALTITUDE_IS | SPEED_IS | TRACK_IS |
669                 CLIMB_IS | STATUS_IS | MODE_IS | CLEAR_IS | REPORT_IS;
670             gpsd_report(LOG_DATA,
671                         "SP-LFEI 0x20: time=%.2f lat=%.2f lon=%.2f alt=%.2f "
672                         "speed=%.2f track=%.2f climb=%.2f "
673                         "mode=%d status=%d mask=%s\n", session->newdata.time,
674                         session->newdata.latitude, session->newdata.longitude,
675                         session->newdata.altitude, session->newdata.speed,
676                         session->newdata.track, session->newdata.climb,
677                         session->newdata.mode, session->gpsdata.status,
678                         gpsd_maskdump(mask));
679             break;
680         case 0x23:              /* Compact Super Packet */
681             session->driver.tsip.req_compact = 0;
682             /* CSK sez "i don't trust this to not be oversized either." */
683             if (len < 29)
684                 break;
685             ul1 = getbeul(buf, 1);      /* time */
686             s1 = getbesw(buf, 5);       /* tsip.gps_week */
687             u1 = getub(buf, 7); /* utc offset */
688             u2 = getub(buf, 8); /* fix flags */
689             sl1 = getbesl(buf, 9);      /* latitude */
690             ul2 = getbeul(buf, 13);     /* longitude */
691             sl3 = getbesl(buf, 17);     /* altitude */
692             s2 = getbesw(buf, 21);      /* east velocity */
693             s3 = getbesw(buf, 23);      /* north velocity */
694             s4 = getbesw(buf, 25);      /* up velocity */
695             gpsd_report(LOG_INF, "CSP %u %d %u %u %d %u %d %d %d %d\n", ul1,
696                         s1, u1, u2, sl1, ul2, sl3, s2, s3, s4);
697             session->context->gps_week = s1;
698             if ((int)u1 > 10) {
699                 session->context->leap_seconds = (int)u1;
700                 session->context->valid |= LEAP_SECOND_VALID;
701             }
702             session->context->gps_week = (unsigned short)s1;
703             session->context->gps_tow = (double)ul1 *1e3;
704             /*@ ignore @*//*@ splint is confused @ */
705             session->newdata.time =
706                 gpstime_to_unix(session->context->gps_week,
707                                 session->context->gps_tow)
708                 - session->context->leap_seconds;
709             /*@ end @*/
710             session->gpsdata.status = STATUS_NO_FIX;
711             session->newdata.mode = MODE_NO_FIX;
712             if ((u2 & 0x01) == (uint8_t) 0) {   /* Fix Available */
713                 session->gpsdata.status = STATUS_FIX;
714                 if ((u2 & 0x02) != (uint8_t) 0) /* DGPS Corrected */
715                     session->gpsdata.status = STATUS_DGPS_FIX;
716                 if ((u2 & 0x04) != (uint8_t) 0) /* Fix Dimension */
717                     session->newdata.mode = MODE_2D;
718                 else
719                     session->newdata.mode = MODE_3D;
720             }
721             session->newdata.latitude = sl1 * SEMI_2_DEG;
722             /*@i1@*/ session->newdata.longitude = ul2 * SEMI_2_DEG;
723             if (session->newdata.longitude > 180.0)
724                 session->newdata.longitude -= 360.0;
725             session->gpsdata.separation =
726                 wgs84_separation(session->newdata.latitude,
727                                  session->newdata.longitude);
728             session->newdata.altitude =
729                 sl3 * 1e-3 - session->gpsdata.separation;;
730             if ((u2 & 0x20) != (uint8_t) 0)     /* check velocity scaling */
731                 d5 = 0.02;
732             else
733                 d5 = 0.005;
734             d1 = s2 * d5;       /* east velocity m/s */
735             d2 = s3 * d5;       /* north velocity m/s */
736             session->newdata.climb = s4 * d5;   /* up velocity m/s */
737             /*@ -evalorder @*/
738             session->newdata.speed =
739                 sqrt(pow(d2, 2) + pow(d1, 2)) * MPS_TO_KNOTS;
740             /*@ +evalorder @*/
741             if ((session->newdata.track = atan2(d1, d2) * RAD_2_DEG) < 0)
742                 session->newdata.track += 360.0;
743             mask |=
744                 TIME_IS | LATLON_IS | ALTITUDE_IS | SPEED_IS | TRACK_IS |
745                 CLIMB_IS | STATUS_IS | MODE_IS | CLEAR_IS | REPORT_IS;
746             gpsd_report(LOG_DATA,
747                         "SP-CSP 0x23: time=%.2f lat=%.2f lon=%.2f alt=%.2f "
748                         "speed=%.2f track=%.2f climb=%.2f "
749                         "mode=%d status=%d mask=%s\n", session->newdata.time,
750                         session->newdata.latitude, session->newdata.longitude,
751                         session->newdata.altitude, session->newdata.speed,
752                         session->newdata.track, session->newdata.climb,
753                         session->newdata.mode, session->gpsdata.status,
754                         gpsd_maskdump(mask));
755             break;
756
757         case 0xab:              /* Thunderbolt Timing Superpacket */
758             if (len != 17) {
759                 gpsd_report(4, "pkt 0xab len=%d\n", len);
760                 break;
761             }
762             session->driver.tsip.last_41 = now; /* keep timestamp for request */
763             ul1 = getbeul(buf, 1);      /* gpstime */
764             s1 = (short)getbeuw(buf, 5);        /* week */
765             s2 = getbesw(buf, 7);       /* leap seconds */
766
767             session->context->gps_week = s1;
768             if ((int)u1 > 10) {
769                 session->context->leap_seconds = (int)s2;
770                 session->context->valid |= LEAP_SECOND_VALID;
771
772                 session->context->gps_week = s1;
773                 session->context->gps_tow = (double)ul1;
774                 session->newdata.time =
775                     gpstime_to_unix((int)s1, session->context->gps_tow)
776                     - (double)s2;
777                 mask |= TIME_IS | CLEAR_IS;
778                 gpsd_report(LOG_DATA, "SP-TTS 0xab time=%.2f mask={TIME}\n",
779                             session->newdata.time);
780             }
781
782             gpsd_report(4, "GPS Time %u %d %d\n", ul1, s1, s2);
783             break;
784
785
786         case 0xac:              /* Thunderbolt Position Superpacket */
787             if (len != 68) {
788                 gpsd_report(4, "pkt 0xac len=%d\n", len);
789
790                 break;
791             }
792             session->newdata.latitude = getbed(buf, 36) * RAD_2_DEG;
793             session->newdata.longitude = getbed(buf, 44) * RAD_2_DEG;
794             session->newdata.altitude = getbed(buf, 52);
795             f1 = getbef(buf, 16);       /* clock bias */
796
797             u1 = getub(buf, 12);        /* GPS Decoding Status */
798             u2 = getub(buf, 1); /* Reciever Mode */
799             if (u1 != (uint8_t) 0) {
800                 session->gpsdata.status = STATUS_NO_FIX;
801                 mask |= STATUS_IS;
802             } else {
803                 if (session->gpsdata.status < STATUS_FIX) {
804                     session->gpsdata.status = STATUS_FIX;
805                     mask |= STATUS_IS;
806                 }
807             }
808
809             /* Decode Fix modes */
810             switch (u2 & 7) {
811             case 0:     /* Auto */
812                 switch (u1) {
813                        /*
814                         * According to the Thunderbolt Manual, the
815                         * first byte of the supplemental timing packet
816                         * simply indicates the configuration of the
817                         * device, not the actual lock, so we need to
818                         * look at the decode status.
819                         */
820                        case 0:   /* "Doing Fixes" */
821                          session->newdata.mode = MODE_3D;
822                          break;
823                        case 0x0B: /* "Only 3 usable sats" */
824                          session->newdata.mode = MODE_2D;
825                          break;
826                        case 0x1:   /* "Don't have GPS time" */
827                        case 0x3:   /* "PDOP is too high" */
828                        case 0x8:   /* "No usable sats" */
829                        case 0x9:   /* "Only 1 usable sat" */
830                        case 0x0A:  /* "Only 2 usable sats */
831                        case 0x0C:  /* "The chosen sat is unusable" */
832                        case 0x10:  /* TRAIM rejected the fix */
833                        default:
834                           session->newdata.mode = MODE_NO_FIX;
835                 }
836                 break;
837             case 6:             /* Clock Hold 2D */
838             case 3:             /* 2D Position Fix */
839                 //session->gpsdata.status = STATUS_FIX;
840                 session->newdata.mode = MODE_2D;
841                 break;
842             case 7:             /* Thunderbolt overdetermined clock */
843             case 4:             /* 3D position Fix */
844                 //session->gpsdata.status = STATUS_FIX;
845                 session->newdata.mode = MODE_3D;
846                 break;
847             default:
848                 //session->gpsdata.status = STATUS_NO_FIX;
849                 session->newdata.mode = MODE_NO_FIX;
850                 break;
851             }
852
853             mask |= LATLON_IS | ALTITUDE_IS | MODE_IS | REPORT_IS;
854             gpsd_report(LOG_DATA, "SP-TPS 0xac "
855                         "time=%.2f lat=%.2f lon=%.2f alt=%.2f mask=%s\n",
856                         session->newdata.time,
857                         session->newdata.latitude,
858                         session->newdata.longitude,
859                         session->newdata.altitude, gpsd_maskdump(mask));
860             break;
861
862
863         default:
864             gpsd_report(LOG_WARN, "Unhandled TSIP superpacket type 0x%02x\n",
865                         u1);
866         }
867         break;
868     case 0xbb:                  /* Navigation Configuration */
869         if (len != 40)
870             break;
871         u1 = getub(buf, 0);     /* Subcode */
872         u2 = getub(buf, 1);     /* Operating Dimension */
873         u3 = getub(buf, 2);     /* DGPS Mode */
874         u4 = getub(buf, 3);     /* Dynamics Code */
875         f1 = getbef(buf, 5);    /* Elevation Mask */
876         f2 = getbef(buf, 9);    /* AMU Mask */
877         f3 = getbef(buf, 13);   /* DOP Mask */
878         f4 = getbef(buf, 17);   /* DOP Switch */
879         u5 = getub(buf, 21);    /* DGPS Age Limit */
880         gpsd_report(LOG_INF,
881                     "Navigation Configuration %u %u %u %u %f %f %f %f %u\n",
882                     u1, u2, u3, u4, f1, f2, f3, f4, u5);
883         break;
884     default:
885         gpsd_report(LOG_WARN, "Unhandled TSIP packet type 0x%02x\n", id);
886         break;
887     }
888
889     /* see if it is time to send some request packets for reports that */
890     /* the receiver won't send at fixed intervals */
891
892     if ((now - session->driver.tsip.last_41) > 5) {
893         /* Request Current Time */
894         (void)tsip_write(session, 0x21, buf, 0);
895         session->driver.tsip.last_41 = now;
896     }
897
898     if ((now - session->driver.tsip.last_6d) > 5) {
899         /* Request GPS Receiver Position Fix Mode */
900         (void)tsip_write(session, 0x24, buf, 0);
901         session->driver.tsip.last_6d = now;
902     }
903
904     if ((now - session->driver.tsip.last_48) > 60) {
905         /* Request GPS System Message */
906         (void)tsip_write(session, 0x28, buf, 0);
907         session->driver.tsip.last_48 = now;
908     }
909
910     if ((now - session->driver.tsip.last_5c) >= 5) {
911         /* Request Current Satellite Tracking Status */
912         putbyte(buf, 0, 0x00);  /* All satellites */
913         (void)tsip_write(session, 0x3c, buf, 1);
914         session->driver.tsip.last_5c = now;
915     }
916
917     if ((now - session->driver.tsip.last_46) > 5) {
918         /* Request Health of Receiver */
919         (void)tsip_write(session, 0x26, buf, 0);
920         session->driver.tsip.last_46 = now;
921     }
922 #if USE_SUPERPACKET
923     if ((session->driver.tsip.req_compact > 0) &&
924         ((now - session->driver.tsip.req_compact) > 5)) {
925         /* Compact Superpacket requested but no response */
926         session->driver.tsip.req_compact = 0;
927         gpsd_report(LOG_WARN, "No Compact Super Packet, use LFwEI\n");
928
929         /* Request LFwEI Super Packet */
930         putbyte(buf, 0, 0x20);
931         putbyte(buf, 1, 0x01);  /* enabled */
932         (void)tsip_write(session, 0x8e, buf, 2);
933     }
934 #endif /* USE_SUPERPACKET */
935
936     return mask;
937 }
938
939 static gps_mask_t tsip_parse_input(struct gps_device_t *session)
940 {
941     gps_mask_t st;
942
943     if (session->packet.type == TSIP_PACKET) {
944         st = tsip_analyze(session);
945         session->gpsdata.dev.driver_mode = MODE_BINARY;
946         return st;
947 #ifdef EVERMORE_ENABLE
948     } else if (session->packet.type == EVERMORE_PACKET) {
949         (void)gpsd_switch_driver(session, "EverMore binary");
950         st = evermore_parse(session, session->packet.outbuffer,
951                             session->packet.outbuflen);
952         session->gpsdata.dev.driver_mode = MODE_BINARY;
953         return st;
954 #endif /* EVERMORE_ENABLE */
955 #ifdef SIRF_ENABLE
956         /*
957          * mrd reported that once every couple of weeks his SiRF was flipping
958          * into Trimble binary mode and not recovering.  Damn Trimble for not
959          * checksumming their packets, it makes false positives hard to reject.
960          * This should enable the SiRF to recover.
961          */
962     } else if (session->packet.type == SIRF_PACKET) {
963         (void)gpsd_switch_driver(session, "SiRF binary");
964         st = sirf_parse(session, session->packet.outbuffer,
965                         session->packet.outbuflen);
966         session->gpsdata.dev.driver_mode = MODE_BINARY;
967         return st;
968 #endif /* SIRF_ENABLE */
969     } else
970         return 0;
971 }
972
973 #ifdef ALLOW_CONTROLSEND
974 static ssize_t tsip_control_send(struct gps_device_t *session,
975                                  char *buf, size_t buflen)
976 /* not used by the daemon, it's for gpsctl and friends */
977 {
978     return (ssize_t) tsip_write(session,
979                                 (unsigned int)buf[0],
980                                 (unsigned char *)buf + 1, buflen - 1);
981 }
982 #endif /* ALLOW_CONTROLSEND */
983
984 static void tsip_event_hook(struct gps_device_t *session, event_t event)
985 {
986     /* FIX-ME: Resending this might not be needed on reactivation */
987     if (event == event_identified && event == event_reactivate) {
988         unsigned char buf[100];
989
990         /* I/O Options */
991         putbyte(buf, 0, 0x1e);  /* Position: DP, MSL, LLA */
992         putbyte(buf, 1, 0x02);  /* Velocity: ENU */
993         putbyte(buf, 2, 0x00);  /* Time: GPS */
994         putbyte(buf, 3, 0x08);  /* Aux: dBHz */
995         (void)tsip_write(session, 0x35, buf, 4);
996     }
997     if (event == event_configure) {
998         unsigned char buf[100];
999         union int_float i_f;
1000
1001         switch (session->packet.counter) {
1002         case 0:
1003             /*
1004              * TSIP is ODD parity 1 stopbit, save original values and
1005              * change it Thunderbolts and Copernicus use
1006              * 8N1... which isn't exactly a good idea due to the
1007              * fragile wire format.  We must divine a clever
1008              * heuristic to decide if the parity change is required.
1009              */
1010             session->driver.tsip.parity = session->gpsdata.dev.parity;
1011             session->driver.tsip.stopbits =
1012                 (uint) session->gpsdata.dev.stopbits;
1013             // gpsd_set_speed(session, session->gpsdata.dev.baudrate, 'O', 1);
1014             break;
1015
1016         case 1:
1017             /*@ -shiftimplementation @*/
1018             /* Request Software Versions */
1019             (void)tsip_write(session, 0x1f, NULL, 0);
1020             /* Request Current Time */
1021             (void)tsip_write(session, 0x21, NULL, 0);
1022             /* Set Operating Parameters */
1023             /* - dynamic code: land */
1024             putbyte(buf, 0, 0x01);
1025             /* - elevation mask */
1026             i_f.f = 5.0 * DEG_2_RAD;
1027             putbelong(buf, 1, i_f.i);
1028             /* - signal level mask */
1029             i_f.f = 6.0;
1030             putbelong(buf, 5, i_f.i);
1031             /* - PDOP mask */
1032             i_f.f = 8.0;
1033             putbelong(buf, 9, i_f.i);
1034             /* - PDOP switch */
1035             i_f.f = 6.0;
1036             putbelong(buf, 13, i_f.i);
1037             /*@ +shiftimplementation @*/
1038             (void)tsip_write(session, 0x2c, buf, 17);
1039             /* Set Position Fix Mode (auto 2D/3D) */
1040             putbyte(buf, 0, 0x00);
1041             (void)tsip_write(session, 0x22, buf, 1);
1042             /* Request GPS Systems Message */
1043             (void)tsip_write(session, 0x28, NULL, 0);
1044             /* Request Current Datum Values */
1045             (void)tsip_write(session, 0x37, NULL, 0);
1046             putbyte(buf, 0, 0x15);
1047             (void)tsip_write(session, 0x8e, buf, 1);
1048             /* Request Navigation Configuration */
1049             putbyte(buf, 0, 0x03);
1050             (void)tsip_write(session, 0xbb, buf, 1);
1051             break;
1052         }
1053     }
1054     if (event == event_deactivate) {
1055         /* restore saved parity and stopbits when leaving TSIP mode */
1056         gpsd_set_speed(session,
1057                        session->gpsdata.dev.baudrate,
1058                        session->driver.tsip.parity,
1059                        session->driver.tsip.stopbits);
1060     }
1061 }
1062
1063 #ifdef ALLOW_RECONFIGURE
1064 static bool tsip_speed_switch(struct gps_device_t *session,
1065                               speed_t speed, char parity, int stopbits)
1066 {
1067     unsigned char buf[100];
1068
1069     switch (parity) {
1070     case 'E':
1071     case 2:
1072         parity = (char)2;
1073         break;
1074     case 'O':
1075     case 1:
1076         parity = (char)1;
1077         break;
1078     case 'N':
1079     case 0:
1080     default:
1081         parity = (char)0;
1082         break;
1083     }
1084
1085     putbyte(buf, 0, 0xff);      /* current port */
1086     putbyte(buf, 1, (round(log((double)speed / 300) / M_LN2)) + 2);     /* input dev.baudrate */
1087     putbyte(buf, 2, getub(buf, 1));     /* output baudrate */
1088     putbyte(buf, 3, 3);         /* character width (8 bits) */
1089     putbyte(buf, 4, parity);    /* parity (normally odd) */
1090     putbyte(buf, 5, stopbits - 1);      /* stop bits (normally 1 stopbit) */
1091     putbyte(buf, 6, 0);         /* flow control (none) */
1092     putbyte(buf, 7, 0x02);      /* input protocol (TSIP) */
1093     putbyte(buf, 8, 0x02);      /* output protocol (TSIP) */
1094     putbyte(buf, 9, 0);         /* reserved */
1095     (void)tsip_write(session, 0xbc, buf, 10);
1096
1097     return true;                /* it would be nice to error-check this */
1098 }
1099
1100 static void tsip_mode(struct gps_device_t *session, int mode)
1101 {
1102     unsigned char buf[16];
1103
1104     if (mode == MODE_NMEA) {
1105         /* First turn on the NMEA messages we want */
1106
1107         putbyte(buf, 0, 0x00);  /* subcode 0 */
1108         putbyte(buf, 1, 0x01);  /* 1-second fix interval */
1109         putbyte(buf, 2, 0x00);  /* Reserved */
1110         putbyte(buf, 3, 0x00);  /* Reserved */
1111         putbyte(buf, 4, 0x01);  /* 0=RMC, 1-7=Reserved */
1112         putbyte(buf, 5, 0x19);  /* 0=GGA, 1=GGL, 2=VTG, 3=GSV, */
1113         /* 4=GSA, 5=ZDA, 6-7=Reserved  */
1114
1115         (void)tsip_write(session, 0x7A, buf, 6);
1116
1117         /* Now switch to NMEA mode */
1118
1119         memset(buf, 0, sizeof(buf));
1120
1121         putbyte(buf, 0, 0xff);  /* current port */
1122         putbyte(buf, 1, 0x06);  /* 4800 bps input */
1123         putbyte(buf, 2, 0x06);  /* 4800 bps output */
1124         putbyte(buf, 3, 0x03);  /* 8 data bits */
1125         putbyte(buf, 4, 0x00);  /* No parity */
1126         putbyte(buf, 5, 0x00);  /* 1 stop bit */
1127         putbyte(buf, 6, 0x00);  /* No flow control */
1128         putbyte(buf, 7, 0x02);  /* Input protocol TSIP */
1129         putbyte(buf, 8, 0x04);  /* Output protocol NMEA */
1130         putbyte(buf, 9, 0x00);  /* Reserved */
1131
1132         (void)tsip_write(session, 0xBC, buf, 10);
1133
1134     } else if (mode == MODE_BINARY) {
1135         /* The speed switcher also puts us back in TSIP, so call it */
1136         /* with the default 9600 8O1. */
1137         // FIX-ME: Should preserve the current speed.
1138         // (void)tsip_speed_switch(session, 9600, 'O', 1);
1139         ;
1140
1141     } else {
1142         gpsd_report(LOG_ERROR, "unknown mode %i requested\n", mode);
1143     }
1144 }
1145 #endif /* ALLOW_RECONFIGURE */
1146
1147 #ifdef NTPSHM_ENABLE
1148 static double tsip_ntp_offset(struct gps_device_t *session)
1149 {
1150     /* FIX-ME: is a constant offset right here? */
1151     return 0.075;
1152 }
1153 #endif /* NTPSHM_ENABLE */
1154
1155 /* this is everything we export */
1156 /* *INDENT-OFF* */
1157 const struct gps_type_t tsip_binary =
1158 {
1159     .type_name      = "Trimble TSIP",   /* full name of type */
1160     .packet_type    = TSIP_PACKET,      /* associated lexer packet type */
1161     .trigger    = NULL,         /* no trigger */
1162     .channels       = TSIP_CHANNELS,    /* consumer-grade GPS */
1163     .probe_detect   = tsip_detect,      /* probe for 9600O81 device */
1164     .get_packet     = generic_get,      /* use the generic packet getter */
1165     .parse_packet   = tsip_parse_input, /* parse message packets */
1166     .rtcm_writer    = NULL,             /* doesn't accept DGPS corrections */
1167     .event_hook     = tsip_event_hook,  /* ifire on various lifetime events */
1168 #ifdef ALLOW_RECONFIGURE
1169     .speed_switcher = tsip_speed_switch,/* change baud rate */
1170     .mode_switcher  = tsip_mode,        /* there is a mode switcher */
1171     .rate_switcher  = NULL,             /* no rate switcher */
1172     .min_cycle      = 1,                /* not relevant, no rate switcher */
1173 #endif /* ALLOW_RECONFIGURE */
1174 #ifdef ALLOW_CONTROLSEND
1175     .control_send   = tsip_control_send,/* how to send commands */
1176 #endif /* ALLOW_CONTROLSEND */
1177 #ifdef NTPSHM_ENABLE
1178     .ntp_offset     = tsip_ntp_offset,
1179 #endif /* NTPSHM_ENABLE */
1180 };
1181 /* *INDENT-ON* */
1182
1183 #endif /* TSIP_ENABLE */