2 * Handle the Trimble TSIP packet format
3 * by Rob Janssen, PE1CHL.
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.
13 #endif /* S_SPLINT_S */
15 #include "gpsd_config.h"
17 #if defined (HAVE_SYS_SELECT_H)
18 #include <sys/select.h>
20 #if defined(HAVE_SYS_TIME_H)
27 #define USE_SUPERPACKET 1 /* use Super Packet mode? */
29 #define SEMI_2_DEG (180.0 / 2147483647) /* 2^-31 semicircle to deg */
32 #define TSIP_CHANNELS 12
34 static int tsip_write(struct gps_device_t *session,
35 unsigned int id, /*@null@*/ unsigned char *buf,
40 gpsd_report(LOG_IO, "Sent TSIP packet id 0x%02x: %s\n", id,
41 gpsd_hexdump_wrapper(buf, len, LOG_IO));
44 session->msgbuf[0] = '\x10';
45 session->msgbuf[1] = (char)id;
46 ep = session->msgbuf + 2;
48 for (cp = (char *)buf; len-- > 0; cp++) {
56 session->msgbuflen = (size_t) (ep - session->msgbuf);
58 if (gpsd_write(session, session->msgbuf, session->msgbuflen) !=
59 (ssize_t) session->msgbuflen)
67 * see if it looks like a TSIP device (speaking 9600O81) is listening and
68 * return 1 if found, 0 if not
70 static bool tsip_detect(struct gps_device_t *session)
80 unsigned int old_stopbits;
82 gpsd_report(LOG_PROG, "Probing TSIP\n");
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);
89 /* request firmware revision and look for a valid response */
91 putbyte(buf, 0, 0x10);
92 putbyte(buf, 1, 0x1f);
93 putbyte(buf, 2, 0x10);
94 putbyte(buf, 3, 0x03);
96 myfd = session->gpsdata.gps_fd;
97 if (write(myfd, buf, 4) == 4) {
98 for (n = 0; n < 3; n++) {
100 FD_SET(myfd, &fdset);
103 if (select(myfd + 1, &fdset, NULL, NULL, &to) != 1)
105 if (generic_get(session) >= 0) {
106 if (session->packet.type == TSIP_PACKET) {
107 gpsd_report(LOG_RAW, "tsip_detect found\n");
116 /* return serial port to original settings */
117 gpsd_set_speed(session, old_baudrate, old_parity, old_stopbits);
122 static gps_mask_t tsip_analyze(struct gps_device_t *session)
124 int i, j, len, count;
127 uint8_t u1, u2, u3, u4, u5;
128 int16_t s1, s2, s3, s4;
129 int32_t sl1, sl2, sl3;
131 float f1, f2, f3, f4, f5;
132 double d1, d2, d3, d4, d5;
134 union long_double l_d;
136 unsigned char buf[BUFSIZ];
139 if (session->packet.type != TSIP_PACKET) {
140 gpsd_report(LOG_INF, "tsip_analyze packet type %d\n",
141 session->packet.type);
146 if (session->packet.outbuflen < 4 || session->packet.outbuffer[0] != 0x10)
149 /* remove DLE stuffing and put data part of message in buf */
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)
158 (void)snprintf(buf2 + strlen(buf2),
159 sizeof(buf2) - strlen(buf2),
160 "%02x", buf[len++] = session->packet.outbuffer[i]);
164 (void)snprintf(session->gpsdata.tag, sizeof(session->gpsdata.tag),
165 "ID%02x", id = (unsigned)session->packet.outbuffer[1]);
167 gpsd_report(LOG_IO, "TSIP packet id 0x%02x length %d: %s\n", id, len,
171 session->cycle_end_reliable = true;
173 case 0x13: /* Packet Received */
176 gpsd_report(LOG_WARN,
177 "Received packet of type %02x cannot be parsed\n", u1);
179 if ((int)u1 == 0x8e && (int)u2 == 0x23) { /* no Compact Super Packet */
180 gpsd_report(LOG_WARN, "No Compact Super Packet, use LFwEI\n");
182 /* Request LFwEI Super Packet */
183 putbyte(buf, 0, 0x20);
184 putbyte(buf, 1, 0x01); /* enabled */
185 (void)tsip_write(session, 0x8e, buf, 2);
187 #endif /* USE_SUPERPACKET */
189 case 0x41: /* GPS Time */
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;
202 session->newdata.time =
203 gpstime_to_unix(session->context->gps_week,
204 session->context->gps_tow) -
205 session->context->leap_seconds;
208 gpsd_report(LOG_INF, "GPS Time %f %d %f\n", f1, s1, f2);
210 case 0x42: /* Single-Precision Position Fix, XYZ ECEF */
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,
220 case 0x43: /* Velocity Fix, XYZ ECEF */
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,
231 case 0x45: /* Software Version Information */
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,
239 getub(buf, 3), getub(buf, 5), getub(buf, 6), getub(buf,
241 getub(buf, 7), getub(buf, 8));
243 gpsd_report(LOG_INF, "Software version: %s\n", session->subtype);
246 case 0x46: /* Health of Receiver */
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;
256 if (session->gpsdata.status < STATUS_FIX) {
257 session->gpsdata.status = STATUS_FIX;
261 gpsd_report(LOG_PROG, "Receiver health %02x %02x\n", u1, u2);
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))
269 for (i = 0; i < count; i++) {
270 u1 = getub(buf, 5 * i + 1);
271 if ((f1 = getbef(buf, 5 * i + 2)) < 0)
273 for (j = 0; j < TSIP_CHANNELS; j++)
274 if (session->gpsdata.PRN[j] == (int)u1) {
275 session->gpsdata.ss[j] = f1;
278 (void)snprintf(buf2 + strlen(buf2), sizeof(buf2) - strlen(buf2),
279 " %d=%.1f", (int)u1, f1);
281 gpsd_report(LOG_PROG, "Signal Levels (%d):%s\n", count, buf2);
282 mask |= SATELLITE_IS;
284 case 0x48: /* GPS System Message */
286 gpsd_report(LOG_PROG, "GPS System Message: %s\n", buf);
288 case 0x49: /* Almanac Health Page */
290 case 0x4a: /* Single-Precision Position LLA */
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;
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));
314 case 0x4b: /* Machine/Code ID and Additional Status */
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);
322 if ((u3 & 0x01) != (uint8_t) 0 && !session->driver.tsip.superpkt) {
323 gpsd_report(LOG_PROG, "Switching to Super Packet mode\n");
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;
333 #endif /* USE_SUPERPACKET */
335 case 0x4c: /* Operating Parameters Report */
337 case 0x54: /* One Satellite Bias */
339 case 0x55: /* IO Options */
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,
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);
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;
361 #endif /* USE_SUPERPACKET */
363 case 0x56: /* Velocity Fix, East-North-Up (ENU) */
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;
373 session->newdata.speed = sqrt(pow(f2, 2) + pow(f1, 2));
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,
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));
387 case 0x57: /* Information About Last Computed Fix */
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 */
395 if (getub(buf, 0) == 0x01) /* good current fix? */
396 session->context->gps_week = s1;
398 gpsd_report(LOG_INF, "Fix info %02x %02x %d %f\n", u1, u2, s1, f1);
400 case 0x58: /* Satellite System Data/Acknowledge from Receiver */
402 case 0x59: /* Status of Satellite Disable or Ignore Health */
404 case 0x5a: /* Raw Measurement Data */
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);
414 case 0x5b: /* Satellite Ephemeris Status */
416 case 0x5c: /* Satellite Tracking Status */
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 */
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) {
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);
438 session->gpsdata.PRN[i] = session->gpsdata.elevation[i]
439 = session->gpsdata.azimuth[i] = 0;
440 session->gpsdata.ss[i] = 0.0;
442 if (++i == session->gpsdata.satellites_visible) {
443 session->gpsdata.skyview_time = NAN;
444 mask |= SATELLITE_IS; /* last of the series */
446 if (i > session->gpsdata.satellites_visible)
447 session->gpsdata.satellites_visible = i;
450 case 0x5e: /* Additional Fix Status Report */
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))
457 session->driver.tsip.last_6d = now; /* keep timestamp for request */
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.
467 switch (u1 & 7) { /* dimension */
469 //session->gpsdata.status = STATUS_FIX;
470 session->newdata.mode = MODE_2D;
473 //session->gpsdata.status = STATUS_FIX;
474 session->newdata.mode = MODE_3D;
477 //session->gpsdata.status = STATUS_NO_FIX;
478 session->newdata.mode = MODE_NO_FIX;
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);
489 session->gpsdata.dop.gdop =
490 sqrt(pow(session->gpsdata.dop.pdop, 2) +
491 pow(session->gpsdata.dop.tdop, 2));
494 memset(session->gpsdata.used, 0, sizeof(session->gpsdata.used));
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));
502 gpsd_report(LOG_DATA, "AIVSS: 0x6d "
504 "pdop=%.1f hdop=%.1f vdop=%.1f tdop=%.1f gdup=%.1f "
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;
515 case 0x6e: /* Synchronized Measurements */
517 case 0x6f: /* Synchronized Measurements Report */
519 if (len < 20 || getub(buf, 0) != 1 || getub(buf, 1) != 2)
522 s1 = getbesw(buf, 2); /* number of bytes */
523 u1 = getub(buf, 20); /* number of SVs */
525 case 0x70: /* Filter Report */
527 case 0x7a: /* NMEA settings */
529 case 0x82: /* Differential Position Fix Mode */
532 u1 = getub(buf, 0); /* fix mode */
534 if (session->gpsdata.status == STATUS_FIX && (u1 & 0x01) != 0) {
535 session->gpsdata.status = STATUS_DGPS_FIX;
539 gpsd_report(LOG_DATA, "DPFM 0x82 status=%d mask=%s\n",
540 session->gpsdata.status, gpsd_maskdump(mask));
542 case 0x83: /* Double-Precision XYZ Position Fix and Bias Information */
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,
553 case 0x84: /* Double-Precision LLA Position Fix and Bias Information */
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;
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));
581 case 0x8f: /* Super Packet. Well... */
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);
588 switch (u1) { /* sub-packet ID */
589 case 0x15: /* Current Datum Values */
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,
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))
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 */
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);
623 if ((u1 & 0x01) != (uint8_t) 0) /* check velocity scaling */
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 */
631 session->newdata.speed = sqrt(pow(d2, 2) + pow(d1, 2));
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;
653 session->newdata.mode = MODE_3D;
655 session->gpsdata.satellites_used = (int)u3;
657 session->context->leap_seconds = (int)u4;
658 session->context->valid |= LEAP_SECOND_VALID;
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;
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));
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." */
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;
699 session->context->leap_seconds = (int)u1;
700 session->context->valid |= LEAP_SECOND_VALID;
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;
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;
719 session->newdata.mode = MODE_3D;
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 */
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 */
738 session->newdata.speed =
739 sqrt(pow(d2, 2) + pow(d1, 2)) * MPS_TO_KNOTS;
741 if ((session->newdata.track = atan2(d1, d2) * RAD_2_DEG) < 0)
742 session->newdata.track += 360.0;
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));
757 case 0xab: /* Thunderbolt Timing Superpacket */
759 gpsd_report(4, "pkt 0xab len=%d\n", len);
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 */
767 session->context->gps_week = s1;
769 session->context->leap_seconds = (int)s2;
770 session->context->valid |= LEAP_SECOND_VALID;
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)
777 mask |= TIME_IS | CLEAR_IS;
778 gpsd_report(LOG_DATA, "SP-TTS 0xab time=%.2f mask={TIME}\n",
779 session->newdata.time);
782 gpsd_report(4, "GPS Time %u %d %d\n", ul1, s1, s2);
786 case 0xac: /* Thunderbolt Position Superpacket */
788 gpsd_report(4, "pkt 0xac len=%d\n", len);
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 */
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;
803 if (session->gpsdata.status < STATUS_FIX) {
804 session->gpsdata.status = STATUS_FIX;
809 /* Decode Fix modes */
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.
820 case 0: /* "Doing Fixes" */
821 session->newdata.mode = MODE_3D;
823 case 0x0B: /* "Only 3 usable sats" */
824 session->newdata.mode = MODE_2D;
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 */
834 session->newdata.mode = MODE_NO_FIX;
837 case 6: /* Clock Hold 2D */
838 case 3: /* 2D Position Fix */
839 //session->gpsdata.status = STATUS_FIX;
840 session->newdata.mode = MODE_2D;
842 case 7: /* Thunderbolt overdetermined clock */
843 case 4: /* 3D position Fix */
844 //session->gpsdata.status = STATUS_FIX;
845 session->newdata.mode = MODE_3D;
848 //session->gpsdata.status = STATUS_NO_FIX;
849 session->newdata.mode = MODE_NO_FIX;
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));
864 gpsd_report(LOG_WARN, "Unhandled TSIP superpacket type 0x%02x\n",
868 case 0xbb: /* Navigation Configuration */
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 */
881 "Navigation Configuration %u %u %u %u %f %f %f %f %u\n",
882 u1, u2, u3, u4, f1, f2, f3, f4, u5);
885 gpsd_report(LOG_WARN, "Unhandled TSIP packet type 0x%02x\n", id);
889 /* see if it is time to send some request packets for reports that */
890 /* the receiver won't send at fixed intervals */
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;
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;
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;
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;
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;
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");
929 /* Request LFwEI Super Packet */
930 putbyte(buf, 0, 0x20);
931 putbyte(buf, 1, 0x01); /* enabled */
932 (void)tsip_write(session, 0x8e, buf, 2);
934 #endif /* USE_SUPERPACKET */
939 static gps_mask_t tsip_parse_input(struct gps_device_t *session)
943 if (session->packet.type == TSIP_PACKET) {
944 st = tsip_analyze(session);
945 session->gpsdata.dev.driver_mode = MODE_BINARY;
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;
954 #endif /* EVERMORE_ENABLE */
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.
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;
968 #endif /* SIRF_ENABLE */
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 */
978 return (ssize_t) tsip_write(session,
979 (unsigned int)buf[0],
980 (unsigned char *)buf + 1, buflen - 1);
982 #endif /* ALLOW_CONTROLSEND */
984 static void tsip_event_hook(struct gps_device_t *session, event_t event)
986 /* FIX-ME: Resending this might not be needed on reactivation */
987 if (event == event_identified && event == event_reactivate) {
988 unsigned char buf[100];
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);
997 if (event == event_configure) {
998 unsigned char buf[100];
1001 switch (session->packet.counter) {
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.
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);
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 */
1030 putbelong(buf, 5, i_f.i);
1033 putbelong(buf, 9, i_f.i);
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);
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);
1063 #ifdef ALLOW_RECONFIGURE
1064 static bool tsip_speed_switch(struct gps_device_t *session,
1065 speed_t speed, char parity, int stopbits)
1067 unsigned char buf[100];
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);
1097 return true; /* it would be nice to error-check this */
1100 static void tsip_mode(struct gps_device_t *session, int mode)
1102 unsigned char buf[16];
1104 if (mode == MODE_NMEA) {
1105 /* First turn on the NMEA messages we want */
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 */
1115 (void)tsip_write(session, 0x7A, buf, 6);
1117 /* Now switch to NMEA mode */
1119 memset(buf, 0, sizeof(buf));
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 */
1132 (void)tsip_write(session, 0xBC, buf, 10);
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);
1142 gpsd_report(LOG_ERROR, "unknown mode %i requested\n", mode);
1145 #endif /* ALLOW_RECONFIGURE */
1147 #ifdef NTPSHM_ENABLE
1148 static double tsip_ntp_offset(struct gps_device_t *session)
1150 /* FIX-ME: is a constant offset right here? */
1153 #endif /* NTPSHM_ENABLE */
1155 /* this is everything we export */
1157 const struct gps_type_t tsip_binary =
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 */
1183 #endif /* TSIP_ENABLE */