1 /*
2  * Handle the Trimble TSIP packet format
3  * by Rob Janssen, PE1CHL.
4  * Acutime Gold support by Igor Socec <igorsocec@gmail.com>
5  * Trimble RES multi-constellation support by Nuno Goncalves <nunojpg@gmail.com>
6  *
7  * Week counters are not limited to 10 bits. It's unknown what
8  * the firmware is doing to disambiguate them, if anything; it might just
9  * be adding a fixed offset based on a hidden epoch value, in which case
10  * unhappy things will occur on the next rollover.
11  *
12  * This file is Copyright (c) 2010-2019 by the GPSD project
13  * SPDX-License-Identifier: BSD-2-clause
14  */
15 
16 #include "gpsd_config.h"  /* must be before all includes */
17 
18 #include <math.h>
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <stdlib.h>           // For llabs()
22 #include <string.h>
23 #include <time.h>
24 #include <unistd.h>
25 
26 #include "gpsd.h"
27 #include "bits.h"
28 #include "strfuncs.h"
29 #include "timespec.h"
30 
31 #ifdef TSIP_ENABLE
32 // RES SMT 360 has 32 max channels, use 64 for next gen
33 #define TSIP_CHANNELS  64
34 
35 /* defines for Set or Request I/O Options (0x35)
36  * SMT 360 default: IO1_DP|IO1_LLA, IO2_ENU, 0, IO4_DBHZ */
37 // byte 1 Position
38 #define IO1_ECEF 1
39 #define IO1_LLA 2
40 #define IO1_MSL 4
41 #define IO1_DP 0x10
42 // IO1_8F20 not in SMT 360
43 #define IO1_8F20 0x20
44 // byte 2 Velocity
45 #define IO2_VECEF 1
46 #define IO2_ENU 2
47 // byte 3 Timing
48 #define IO3_UTC 1
49 // byte 4 Aux/Reserved
50 #define IO4_RAW 1
51 #define IO4_DBHZ 8
52 
53 #define SEMI_2_DEG      (180.0 / 2147483647)    /* 2^-31 semicircle to deg */
54 
55 void configuration_packets_acutime_gold(struct gps_device_t *session);
56 void configuration_packets_res360(struct gps_device_t *session);
57 void configuration_packets_generic(struct gps_device_t *session);
58 
59 /* convert TSIP SV Type to satellite_t.gnssid and satellite_t.svid
60  * return gnssid directly, svid indirectly through pointer */
tsip_gnssid(unsigned svtype,short prn,unsigned char * svid)61 static unsigned char tsip_gnssid(unsigned svtype, short prn,
62                                  unsigned char *svid)
63 {
64     unsigned char gnssid;
65 
66     *svid = 0;
67 
68     switch (svtype) {
69     case 0:
70         if (0 < prn && 33 > prn) {
71             gnssid = GNSSID_GPS;
72             *svid = prn;
73         } else if (32 < prn && 55 > prn) {
74             // RES SMT 360 and ICM SMT 360 put SBAS in 33-54
75             gnssid = GNSSID_SBAS;
76             *svid = prn + 87;
77         } else if (64 < prn && 97 > prn) {
78             // RES SMT 360 and ICM SMT 360 put GLONASS in 65-96
79             gnssid = GNSSID_GLO;
80             *svid = prn - 64;
81         } else if (96 < prn && 134 > prn) {
82             // RES SMT 360 and ICM SMT 360 put Galileo in 97-133
83             gnssid = GNSSID_GAL;
84             *svid = prn - 96;
85         } else if (119 < prn && 139 > prn) {
86             // Copernicus (II) put SBAS in 120-138
87             gnssid = GNSSID_SBAS;
88             *svid = prn + 87;
89         } else if (183 == prn) {
90             gnssid = GNSSID_QZSS;
91             *svid = 1;
92         } else if (192 >= prn && 193 >= prn) {
93             gnssid = GNSSID_QZSS;
94             *svid = prn - 190;
95         } else if (200 == prn) {
96             gnssid = GNSSID_QZSS;
97             *svid = 4;
98         } else if (200 < prn && 238 > prn) {
99             // BeidDou in 201-237
100             gnssid = GNSSID_BD;
101             *svid = prn - 200;
102         }
103         // else: huh?
104         break;
105     case 1:
106         gnssid = GNSSID_GLO;  // GLONASS
107         *svid = prn - 64;
108         break;
109     case 2:
110         gnssid = GNSSID_BD;  // BeiDou
111         *svid = prn - 200;
112         break;
113     case 3:
114         gnssid = GNSSID_GAL;  // Galileo
115         *svid = prn - 96;
116         break;
117     case 5:
118         gnssid = GNSSID_QZSS;  // QZSS
119         switch (prn) {
120         case 183:
121             *svid = 1;
122             break;
123         case 192:
124             *svid = 2;
125             break;
126         case 193:
127             *svid = 3;
128             break;
129         case 200:
130             *svid = 4;
131             break;
132         default:
133             *svid = prn;
134             break;
135         }
136         break;
137     case 4:
138         // FALLTHROUGH
139     case 6:
140         // FALLTHROUGH
141     case 7:
142         // FALLTHROUGH
143     default:
144         svid = 0;
145         gnssid = 0;
146         break;
147     }
148     return gnssid;
149 }
150 
tsip_write(struct gps_device_t * session,unsigned int id,unsigned char * buf,size_t len)151 static int tsip_write(struct gps_device_t *session,
152                       unsigned int id, unsigned char *buf, size_t len)
153 {
154     char *ep, *cp;
155     char obuf[100];
156     size_t olen = len;
157 
158     session->msgbuf[0] = '\x10';
159     session->msgbuf[1] = (char)id;
160     ep = session->msgbuf + 2;
161     for (cp = (char *)buf; olen-- > 0; cp++) {
162         if (*cp == '\x10')
163             *ep++ = '\x10';
164         *ep++ = *cp;
165     }
166     *ep++ = '\x10';
167     *ep++ = '\x03';
168     session->msgbuflen = (size_t) (ep - session->msgbuf);
169     GPSD_LOG(LOG_PROG, &session->context->errout,
170              "TSIP: Sent packet id 0x%s\n",
171              gpsd_hexdump(obuf, sizeof(obuf), &session->msgbuf[1], len + 1));
172     if (gpsd_write(session, session->msgbuf, session->msgbuflen) !=
173         (ssize_t) session->msgbuflen)
174         return -1;
175 
176     return 0;
177 }
178 
179 /* tsip_detect()
180  *
181  * see if it looks like a TSIP device (speaking 9600O81) is listening and
182  * return 1 if found, 0 if not
183  */
tsip_detect(struct gps_device_t * session)184 static bool tsip_detect(struct gps_device_t *session)
185 {
186     char buf[BUFSIZ];
187     bool ret = false;
188     int myfd;
189     speed_t old_baudrate;
190     char old_parity;
191     unsigned int old_stopbits;
192 
193     old_baudrate = session->gpsdata.dev.baudrate;
194     old_parity = session->gpsdata.dev.parity;
195     old_stopbits = session->gpsdata.dev.stopbits;
196     // FIXME.  Should respect fixed speed/framing
197     gpsd_set_speed(session, 9600, 'O', 1);
198 
199     /* request firmware revision and look for a valid response */
200     putbyte(buf, 0, 0x10);
201     putbyte(buf, 1, 0x1f);
202     putbyte(buf, 2, 0x10);
203     putbyte(buf, 3, 0x03);
204     myfd = session->gpsdata.gps_fd;
205     if (write(myfd, buf, 4) == 4) {
206         unsigned int n;
207         for (n = 0; n < 3; n++) {
208             if (!nanowait(myfd, NS_IN_SEC))
209                 break;
210             if (generic_get(session) >= 0) {
211                 if (session->lexer.type == TSIP_PACKET) {
212                     GPSD_LOG(LOG_RAW, &session->context->errout,
213                              "TSIP: tsip_detect found\n");
214                     ret = true;
215                     break;
216                 }
217             }
218         }
219     }
220 
221     if (!ret)
222         /* return serial port to original settings */
223         gpsd_set_speed(session, old_baudrate, old_parity, old_stopbits);
224 
225     return ret;
226 }
227 
228 /* This is the meat of parsing all the TSIP packets */
tsip_parse_input(struct gps_device_t * session)229 static gps_mask_t tsip_parse_input(struct gps_device_t *session)
230 {
231     int i, j, len, count;
232     gps_mask_t mask = 0;
233     unsigned int id;
234     unsigned short week;
235     uint8_t u1, u2, u3, u4, u5, u6, u7, u8, u9, u10;
236     int16_t s1, s2, s3, s4;
237     int32_t sl1, sl2, sl3;
238     uint32_t ul1, ul2;
239     float f1, f2, f3, f4;
240     double d1, d2, d3, d4, d5;
241     time_t now;
242     unsigned char buf[BUFSIZ];
243     char buf2[BUFSIZ];
244     uint32_t tow;             // time of week in milli seconds
245     double ftow;              // time of week in seconds
246     double temp;              // tempurature in degrees C
247     double fqErr;             // PPS Offset. positive is slow.
248     timespec_t ts_tow;
249     char ts_buf[TIMESPEC_LEN];
250     int bad_len = 0;
251     const char *name;
252 
253     if (session->lexer.type != TSIP_PACKET) {
254         // this should not happen
255         GPSD_LOG(LOG_INF, &session->context->errout,
256                  "TSIP: tsip_analyze packet type %d\n",
257                  session->lexer.type);
258         return 0;
259     }
260 
261     if (session->lexer.outbuflen < 4 || session->lexer.outbuffer[0] != 0x10) {
262         /* packet too short, or does not start with DLE */
263         GPSD_LOG(LOG_INF, &session->context->errout,
264                  "TSIP: tsip_analyze packet bad packet\n");
265         return 0;
266     }
267 
268     // get receive time, first
269     (void)time(&now);
270 
271     /* remove DLE stuffing and put data part of message in buf */
272 
273     memset(buf, 0, sizeof(buf));
274     buf2[len = 0] = '\0';
275     for (i = 2; i < (int)session->lexer.outbuflen; i++) {
276         if (session->lexer.outbuffer[i] == 0x10)
277             if (session->lexer.outbuffer[++i] == 0x03)
278                 break;
279 
280         // FIXME  expensive way to do hex
281         str_appendf(buf2, sizeof(buf2),
282                        "%02x", buf[len++] = session->lexer.outbuffer[i]);
283     }
284 
285     id = (unsigned)session->lexer.outbuffer[1];
286     GPSD_LOG(LOG_DATA, &session->context->errout,
287              "TSIP: got packet id 0x%02x length %d: %s\n",
288              id, len, buf2);
289 
290     // session->cycle_end_reliable = true;
291     switch (id) {
292     case 0x13:
293         /* Packet Received
294          * Present in:
295          *   pre-2000 models
296          *   ICM SMT 360 (2018)
297          *   RES SMT 360 (2018)
298          * Not present in:
299          *   Copernicus II
300          */
301         if (1 > len) {
302             bad_len = 1;
303             break;
304         }
305         u1 = getub(buf, 0);     // Packet ID of non-parsable packet
306         if (2 <= len) {
307             u2 = getub(buf, 1);     // Data byte 0 of non-parsable packet
308         } else {
309             u2 = 0;
310         }
311         GPSD_LOG(LOG_WARN, &session->context->errout,
312                  "TSIP: Report Packet (0x13): type x%02x %02x "
313                  "cannot be parsed\n",
314                  u1, u2);
315         // ignore the rest of the bad data
316         if ((int)u1 == 0x8e && (int)u2 == 0x23) {
317             /* no Compact Super Packet 0x8e-23 */
318             GPSD_LOG(LOG_WARN, &session->context->errout,
319                      "TSIP: No 0x8e-23, use LFwEI (0x8f-20)\n");
320 
321             /* Request LFwEI Super Packet instead
322              * SMT 360 does not support 0x8e-20 either */
323             putbyte(buf, 0, 0x20);
324             putbyte(buf, 1, 0x01);      /* auto-report */
325             (void)tsip_write(session, 0x8e, buf, 2);
326         }
327         break;
328 
329     case 0x1c:        // Hardware/Software Version Information
330         /* Present in:
331          *   Acutime Gold
332          *   Lassen iQ (2005) fw 1.16+
333          *   Copernicus (2006)
334          *   Copernicus II (2009)
335          *   Thunderbolt E (2012)
336          *   RES SMT 360 (2018)
337          *   ICM SMT 360 (2018)
338          *   RES360 17x22 (2018)
339          *   Acutime 360
340          * Not Present in:
341          *   pre-2000 models
342          *   ACE II (1999)
343          *   ACE III (2000)
344          *   Lassen SQ (2002)
345          *   Lassen iQ (2005) pre fw 1.16
346          */
347         u1 = (uint8_t) getub(buf, 0);
348         // decode by sub-code
349         switch (u1) {
350         case 0x81:
351                 /* Firmware component version information (0x1c-81)
352                  * polled by 0x1c-01
353                  * Present in:
354                  *   Copernicus II (2009)
355                  */
356                 // byte 1, reserved
357                 u2 = getub(buf, 2);       // Major version
358                 u3 = getub(buf, 3);       // Minor version
359                 u4 = getub(buf, 4);       // Build number
360                 u5 = getub(buf, 5);       // Build Month
361                 u6 = getub(buf, 6);       // Build Day
362                 ul1 = getbeu16(buf, 7);   // Build Year
363                 u7 = getub(buf, 9);       // Length of product name
364                 // check for valid module name length
365                 if (40 < u7) {
366                     u7 = 40;
367                 }
368                 // check for valid module name length, again
369                 if (u7 > (len - 10)) {
370                     u7 = len - 10;
371                 }
372                 /* Product name in ASCII */
373                 memcpy(buf2, &buf[10], u7);
374                 buf2[u7] = '\0';
375 
376                 (void)snprintf(session->subtype, sizeof(session->subtype),
377                                "fw %u.%u %u %02u/%02u/%04u %.40s",
378                                u2, u3, u4, u6, u5, ul1, buf2);
379                 GPSD_LOG(LOG_PROG, &session->context->errout,
380                          "TSIP: Firmware version (0x1c-81): %s\n",
381                          session->subtype);
382 
383                 mask |= DEVICEID_SET;
384                 if ('\0' == session->subtype1[0]) {
385                     // request actual subtype1 from 0x1c-83
386                     putbyte(buf, 0, 0x03);
387                     (void)tsip_write(session, 0x1c, buf, 1);
388                 }
389                 break;
390 
391         case 0x83:
392                 /* Hardware component version information (0x1c-83)
393                  * polled by 0x1c-03
394                  * Not Present in:
395                  *   Copernicus II (2009)
396                  */
397                 ul1 = getbeu32(buf, 1);  // Serial number
398                 u2 = getub(buf, 5);      // Build day
399                 u3 = getub(buf, 6);      // Build month
400                 ul2 = getbeu16(buf, 7);  // Build year
401                 u4 = getub(buf, 9);      // Build hour
402                 /* Hardware Code */
403                 session->driver.tsip.hardware_code = getbeu16(buf, 10);
404                 u5 = getub(buf, 12);     /* Length of Hardware ID */
405                 // check for valid module name length
406                 // copernicus ii is 27 long
407                 if (40 < u5) {
408                     u5 = 40;
409                 }
410                 // check for valid module name length, again
411                 if (u5 > (len - 13)) {
412                     u5 = len - 13;
413                 }
414                 memcpy(buf2, &buf[13], u5);
415                 buf2[u5] = '\0';
416 
417                 (void)snprintf(session->subtype1, sizeof(session->subtype1),
418                                "hw %u %02u/%02u/%04u %02u %04u %.40s",
419                                ul1, u2, u3, ul2, u4,
420                                session->driver.tsip.hardware_code,
421                                buf2);
422                 GPSD_LOG(LOG_PROG, &session->context->errout,
423                          "TSIP: Hardware version (0x1c-83): %s\n",
424                          session->subtype1);
425 
426                 mask |= DEVICEID_SET;
427 
428                 /* Detecting device by Hardware Code */
429                 switch (session->driver.tsip.hardware_code) {
430                 case 3001:            // Acutime Gold
431                     session->driver.tsip.subtype = TSIP_ACUTIME_GOLD;
432                     configuration_packets_acutime_gold(session);
433                     break;
434                 case 3023:            // RES SMT 360
435                     session->driver.tsip.subtype = TSIP_RESSMT360;
436                     configuration_packets_res360(session);
437                     break;
438                 case 3026:            // ICM SMT 360
439                     session->driver.tsip.subtype = TSIP_ICMSMT360;
440                     configuration_packets_res360(session);
441                     break;
442                 case 3031:            // RES360 17x22
443                     session->driver.tsip.subtype = TSIP_RES36017x22;
444                     configuration_packets_res360(session);
445                     break;
446                 case 1001:            // Lassen iQ
447                     // FALLTHROUGH
448                 case 1002:            // Copernicus, Copernicus II
449                     // FALLTHROUGH
450                 case 3007:            // Thunderbolt E
451                     // FALLTHROUGH
452                 case 3032:            // Acutime 360
453                     // FALLTHROUGH
454                 default:
455                     configuration_packets_generic(session);
456                     break;
457                 }
458                 break;
459         default:
460                 GPSD_LOG(LOG_ERROR, &session->context->errout,
461                          "TSIP: Unhandled subpacket ID 0x1c-%x\n", u1);
462                 break;
463         }
464         break;
465     case 0x41:
466         /* GPS Time (0x41).  polled by 0x21
467          * Note: this is not the time of current fix
468          * Present in:
469          *   pre-2000 models
470          *   Copernicus II (2009)
471          *   ICM SMT 360 (2018)
472          *   RES SMT 360 (2018)
473          */
474         if (len != 10) {
475             bad_len = 10;
476             break;
477         }
478         session->driver.tsip.last_41 = now;     /* keep timestamp for request */
479         ftow = getbef32((char *)buf, 0);        /* gpstime */
480         week = getbeu16(buf, 4);        /* week */
481         f2 = getbef32((char *)buf, 6);  /* leap seconds */
482         if (ftow >= 0.0 && f2 > 10.0) {
483             session->context->leap_seconds = (int)round(f2);
484             session->context->valid |= LEAP_SECOND_VALID;
485             DTOTS(&ts_tow, ftow);
486             session->newdata.time =
487                 gpsd_gpstime_resolv(session, week, ts_tow);
488             mask |= TIME_SET | NTPTIME_IS;
489             /* Note: this is not the time of current fix
490              * Do not use in tsip.last_tow */
491         }
492         GPSD_LOG(LOG_PROG, &session->context->errout,
493                  "TSIP: GPS Time (0x41): tow %.2f week %u ls %.1f %s\n",
494                  ftow, week, f2,
495                  timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)));
496         break;
497     case 0x42:
498         /* Single-Precision Position Fix, XYZ ECEF
499          * Present in:
500          *   pre-2000 models
501          *   Copernicus II (2009)
502          *   ICM SMT 360 (2018)
503          *   RES SMT 360 (2018)
504          */
505         if (16 > len) {
506             bad_len = 16;
507             break;
508         }
509         session->newdata.ecef.x = getbef32((char *)buf, 0);  /* X */
510         session->newdata.ecef.y = getbef32((char *)buf, 4);  /* Y */
511         session->newdata.ecef.z = getbef32((char *)buf, 8);  /* Z */
512         ftow = getbef32((char *)buf, 12); /* time-of-fix */
513         DTOTS(&ts_tow, ftow);
514         session->newdata.time = gpsd_gpstime_resolv(session,
515                                                     session->context->gps_week,
516                                                     ts_tow);
517         GPSD_LOG(LOG_PROG, &session->context->errout,
518                  "TSIP: SP-XYZ (0x42): %f %f %f ftow %f\n",
519                  session->newdata.ecef.x,
520                  session->newdata.ecef.y,
521                  session->newdata.ecef.z,
522                  ftow);
523         mask = ECEF_SET | TIME_SET | NTPTIME_IS;
524         if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
525             mask |= CLEAR_IS;
526             session->driver.tsip.last_tow = ts_tow;
527         }
528         break;
529     case 0x43:
530         /* Velocity Fix, XYZ ECEF
531          * Present in:
532          *   pre-2000 models
533          *   ICM SMT 360 (2018)
534          *   RES SMT 360 (2018)
535          * Not Present in:
536          *   Copernicus II (2009)
537          */
538         if (len != 20) {
539             bad_len = 20;
540             break;
541         }
542         session->newdata.ecef.vx = getbef32((char *)buf, 0);  // X velocity
543         session->newdata.ecef.vy = getbef32((char *)buf, 4);  // Y velocity
544         session->newdata.ecef.vz = getbef32((char *)buf, 8);  // Z velocity
545         f4 = getbef32((char *)buf, 12); /* bias rate */
546         ftow = getbef32((char *)buf, 16); /* time-of-fix */
547         DTOTS(&ts_tow, ftow);
548         session->newdata.time = gpsd_gpstime_resolv(session,
549                                                     session->context->gps_week,
550                                                     ts_tow);
551         GPSD_LOG(LOG_PROG, &session->context->errout,
552                  "TSIP: Vel XYZ (0x43): %f %f %f %f ftow %f\n",
553                  session->newdata.ecef.vx,
554                  session->newdata.ecef.vy,
555                  session->newdata.ecef.vz,
556                  f4, ftow);
557         mask = VECEF_SET | TIME_SET | NTPTIME_IS;
558         if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
559             mask |= CLEAR_IS;
560             session->driver.tsip.last_tow = ts_tow;
561         }
562         break;
563     case 0x45:
564         /* Software Version Information (0x45)
565          * Present in:
566          *   pre-2000 models
567          *   ACE II (1999)
568          *   ACE III (2000)
569          *   Lassen SQ (2002)
570          *   Lassen iQ (2005)
571          *   Copernicus II (2009)
572          *   ICM SMT 360
573          *   RES SMT 360
574          *   Probably all TSIP
575          */
576         if (10 > len) {
577             bad_len = 10;
578             break;
579         }
580         // convert 2 digit years to 4 digit years
581         ul1 = getub(buf, 3);
582         if (80 > ul1) {
583             ul1 += 2000;
584         } else {
585             ul1 += 1900;
586         }
587         ul2 = getub(buf, 8);
588         if (80 > ul2) {
589             ul2 += 2000;
590         } else {
591             ul2 += 1900;
592         }
593         /* ACE calls these "NAV processor firmware" and
594          * "SIG processor firmware".
595          * RES SMT 360 calls these "application" and "GPS core".
596          */
597         (void)snprintf(session->subtype, sizeof(session->subtype),
598                        "sw %d.%d %02d/%02d/%04d hw %d.%d %02d/%02d/%04d",
599                        getub(buf, 0),
600                        getub(buf, 1),
601                        getub(buf, 4),
602                        getub(buf, 2),
603                        ul1,
604                        getub(buf, 5),
605                        getub(buf, 6),
606                        getub(buf, 9),
607                        getub(buf, 7),
608                        ul2);
609         GPSD_LOG(LOG_PROG, &session->context->errout,
610                  "TSIP: Software version (0x45): %s\n", session->subtype);
611         mask |= DEVICEID_SET;
612         break;
613     case 0x46:
614         /* Health of Receiver (0x46).  Poll with 0x26
615          * Present in:
616          *   pre-2000 models
617          *   Copernicus II (2009)
618          *   ICM SMT 360 (2018)
619          *   RES SMT 360 (2018)
620          *   all models?
621          * RES SMT 360 says use 0x8f-ab or 0x8f-ac instead
622          */
623         if ( 2 > len) {
624             bad_len = 2;
625             break;
626         }
627         session->driver.tsip.last_46 = now;
628         u1 = getub(buf, 0);     /* Status code */
629         /* Error codes, model dependent
630          * 0x01 -- no battery, always set on RES SMT 360
631          * 0x10 -- antenna fault
632          * 0x20 -- antenna is shorted
633          */
634         u2 = getub(buf, 1);
635         if ((uint8_t)0 != u1) {
636             session->gpsdata.status = STATUS_NO_FIX;
637             mask |= STATUS_SET;
638         } else if (session->gpsdata.status < STATUS_FIX) {
639             session->gpsdata.status = STATUS_FIX;
640             mask |= STATUS_SET;
641         }
642         GPSD_LOG(LOG_PROG, &session->context->errout,
643                  "TSIP: Receiver Health (0x46): %x %x\n", u1, u2);
644         break;
645     case 0x47:
646         /* Signal Levels for all Satellites
647          * Present in:
648          *   pre-2000 models
649          *   Copernicus II (2009)
650          *   ICM SMT 360 (2018)
651          *   RES SMT 360 (2018)
652          */
653         if (1 > len) {
654             bad_len = 1;
655             break;
656         }
657         gpsd_zero_satellites(&session->gpsdata);
658         /* satellite count, RES SMT 360 doc says 12 max */
659         count = (int)getub(buf, 0);
660         if (len != (5 * count + 1)) {
661             bad_len = 5 * count + 1;
662             break;
663         }
664         buf2[0] = '\0';
665         for (i = 0; i < count; i++) {
666             u1 = getub(buf, 5 * i + 1);
667             if ((f1 = getbef32((char *)buf, 5 * i + 2)) < 0)
668                 f1 = 0.0;
669             for (j = 0; j < TSIP_CHANNELS; j++)
670                 if (session->gpsdata.skyview[j].PRN == (short)u1) {
671                     session->gpsdata.skyview[j].ss = f1;
672                     break;
673                 }
674             str_appendf(buf2, sizeof(buf2), " %d=%.1f", (int)u1, f1);
675         }
676         GPSD_LOG(LOG_PROG, &session->context->errout,
677                  "TSIP: Signal Levels (0x47): (%d):%s\n", count, buf2);
678         mask |= SATELLITE_SET;
679         break;
680     case 0x48:
681         /* GPS System Message
682          * Present in:
683          *   pre-2000 models
684          * Not Present in:
685          *   Copernicus II (2009)
686          *   ICM SMT 360 (2018)
687          *   RES SMT 360 (2018)
688          */
689         buf[len] = '\0';
690         GPSD_LOG(LOG_PROG, &session->context->errout,
691                  "TSIP: GPS System Message (0x48): %s\n", buf);
692         break;
693     case 0x4a:
694         /* Single-Precision Position LLA
695          * Present in:
696          *   pre-2000 models
697          *   Copernicus II (2009)
698          *   ICM SMT 360 (2018)
699          *   RES SMT 360 (2018)
700          */
701         if (len != 20) {
702             bad_len = 20;
703             break;
704         }
705         session->newdata.latitude = getbef32((char *)buf, 0) * RAD_2_DEG;
706         session->newdata.longitude = getbef32((char *)buf, 4) * RAD_2_DEG;
707         /* depending on GPS config, could be either WGS84 or MSL */
708         d1 = getbef32((char *)buf, 8);
709         if (0 == session->driver.tsip.alt_is_msl) {
710             session->newdata.altHAE = d1;
711         } else {
712             session->newdata.altMSL = d1;
713         }
714 
715         //f1 = getbef32((char *)buf, 12);       clock bias */
716         ftow = getbef32((char *)buf, 16);       /* time-of-fix */
717         if ((session->context->valid & GPS_TIME_VALID)!=0) {
718             DTOTS(&ts_tow, ftow);
719             session->newdata.time =
720                 gpsd_gpstime_resolv(session, session->context->gps_week,
721                                     ts_tow);
722             mask |= TIME_SET | NTPTIME_IS;
723             if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
724                 mask |= CLEAR_IS;
725                 session->driver.tsip.last_tow = ts_tow;
726             }
727         }
728         // this seems to be often first in cycle
729         // REPORT_IS here breaks reports in read-only mode
730         mask |= LATLON_SET | ALTITUDE_SET;
731         GPSD_LOG(LOG_DATA, &session->context->errout,
732                  "TSIP: SP-LLA (0x4a): time=%s lat=%.2f lon=%.2f "
733                  "alt=%.2f\n",
734                  timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
735                  session->newdata.latitude,
736                  session->newdata.longitude, d1);
737         break;
738     case 0x4b:
739         /* Machine/Code ID and Additional Status (0x4b)
740          * polled by i0x25 or 0x26.  Sent with 0x46.
741          * Present in:
742          *   pre-2000 models
743          *   Copernicus II (2009)
744          *   ICM SMT 360 (2018)
745          *   RES SMT 360 (2018)
746          *   all receivers?
747          */
748         if (len != 3) {
749             bad_len = 3;
750             break;
751         }
752         session->driver.tsip.machine_id = getub(buf, 0);  /* Machine ID */
753         /* Status 1
754          * bit 1 -- No RTC at power up
755          * bit 3 -- almanac not complete and current */
756         u2 = getub(buf, 1);
757         u3 = getub(buf, 2);     /* Status 2/Superpacket Support */
758         GPSD_LOG(LOG_PROG, &session->context->errout,
759                  "TSIP: Machine ID (0x4b): %02x %02x %02x\n",
760                  session->driver.tsip.machine_id,
761                  u2, u3);
762 
763         if ('\0' == session->subtype[0]) {
764             // better than nothing
765             switch (session->driver.tsip.machine_id) {
766             case 1:
767                 // should use better name from superpacket
768                 name = " SMT 360";
769                 /* request actual subtype from 0x1c-81
770                  * which in turn requests 0x1c-83 */
771                 putbyte(buf, 0, 0x01);
772                 (void)tsip_write(session, 0x1c, buf, 1);
773                 break;
774             case 0x32:
775                 name = " Acutime 360";
776                 break;
777             case 0x5a:
778                 name = " Lassen iQ";
779                 /* request actual subtype from 0x1c-81
780                  * which in turn requests 0x1c-83.
781                  * Only later firmware Lassen iQ supports this */
782                 putbyte(buf, 0, 0x01);
783                 (void)tsip_write(session, 0x1c, buf, 1);
784                 break;
785             case 0x61:
786                 name = " Acutime 2000";
787                 break;
788             case 0x62:
789                 name = " ACE UTC";
790                 break;
791             case 0x96:
792                 // Also Copernicus II
793                 name = " Copernicus, Thunderbolt E";
794                 /* so request actual subtype from 0x1c-81
795                  * which in turn requests 0x1c-83 */
796                 putbyte(buf, 0, 0x01);
797                 (void)tsip_write(session, 0x1c, buf, 1);
798                 break;
799             default:
800                  name = "";
801             }
802             (void)snprintf(session->subtype, sizeof(session->subtype),
803                            "Machine ID x%x%s",
804                            session->driver.tsip.machine_id, name);
805         }
806         if (u3 != session->driver.tsip.superpkt) {
807             session->driver.tsip.superpkt = u3;
808             GPSD_LOG(LOG_PROG, &session->context->errout,
809                      "TSIP: Switching to Super Packet mode %d\n", u3);
810             switch (u3){
811             default:
812                 // FALLTHROUGH
813             case 0:
814                 // old Trimble, no superpackets
815                 break;
816             case 1:
817                 // 1 == superpacket is acutime 360, support 0x8f-20
818 
819                 /* set I/O Options for Super Packet output */
820                 /* Position: 8F20, ECEF, DP */
821                 putbyte(buf, 0, IO1_8F20|IO1_DP|IO1_ECEF);
822                 putbyte(buf, 1, 0x00);          /* Velocity: none (via SP) */
823                 putbyte(buf, 2, 0x00);          /* Time: GPS */
824                 putbyte(buf, 3, IO4_DBHZ);      /* Aux: dBHz */
825                 (void)tsip_write(session, 0x35, buf, 4);
826                 break;
827             case 2:
828                 // 2 == SMT 360, no 0x8f-20
829                 break;
830             }
831         }
832         break;
833     case 0x4c:
834         /* Operating Parameters Report (0x4c).  Polled by 0x2c
835          * Present in:
836          *   pre-2000 models
837          *   Lassen iQ, but not documented
838          * Not Present in:
839          *   Copernicus II (2009)
840          *   ICM SMT 360 (2018)
841          *   RES SMT 360 (2018)
842          */
843         if (len != 17) {
844             bad_len = 17;
845             break;
846         }
847         u1 = getub(buf, 0);               // Dynamics Code
848         f1 = getbef32((char *)buf, 1);    // Elevation Mask
849         f2 = getbef32((char *)buf, 5);    // Signal Level Mask
850         f3 = getbef32((char *)buf, 9);    // PDOP Mask
851         f4 = getbef32((char *)buf, 13);   // PDOP Switch
852         GPSD_LOG(LOG_PROG, &session->context->errout,
853                  "TSIP: Operating Params (0x4c): x%02x %f %f %f %f\n",
854                  u1, f1, f2, f3, f4);
855         break;
856     case 0x54:
857         /* Bias and Bias Rate Report (0x54)
858          * Present in:
859          *   pre-2000 models
860          *   Acutime 360
861          *   ICM SMT 360  (undocumented)
862          *   RES SMT 360  (undocumented)
863          * Not Present in:
864          *   Copernicus II (2009)
865          */
866          {
867             float  bias, bias_rate;
868             bias = getbef32((char *)buf, 0);         // Bias
869             bias_rate = getbef32((char *)buf, 4);    // Bias rate
870             ftow = getbef32((char *)buf, 8);         // tow
871             DTOTS(&ts_tow, ftow);
872             session->newdata.time =
873                 gpsd_gpstime_resolv(session, session->context->gps_week,
874                                     ts_tow);
875             GPSD_LOG(LOG_PROG, &session->context->errout,
876                      "TSIP: Bias and Bias Rate Report (0x54) %f %f %f\n",
877                      bias, bias_rate, ftow);
878             mask |= TIME_SET | NTPTIME_IS;
879             if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
880                 mask |= CLEAR_IS;
881                 session->driver.tsip.last_tow = ts_tow;
882             }
883          }
884          break;
885     case 0x55:
886         /* IO Options (0x55), polled by 0x35
887          * Present in:
888          *   pre-2000 models
889          *   ICM SMT 360 (2018)
890          *   RES SMT 360 (2018)
891          *   all TSIP?
892          *
893          * Lassen iQ defaults: 02 02 00 00
894          * RES SMT 360 defaults:  12 02 00 08
895          */
896         if (len != 4) {
897             bad_len = 4;
898             break;
899         }
900         u1 = getub(buf, 0);     /* Position */
901         // decode HAE/MSL from Position byte
902         if (IO1_MSL == (IO1_MSL & u1)) {
903             session->driver.tsip.alt_is_msl = 1;
904         } else {
905             session->driver.tsip.alt_is_msl = 0;
906         }
907         u2 = getub(buf, 1);     /* Velocity */
908         /* Timing
909          * bit 0 - reserved use 0x8e-a2 ?
910          */
911         u3 = getub(buf, 2);
912         /* Aux
913          * bit 0 - packet 0x5a (raw data)
914          * bit 3 -- Output dbHz
915          */
916         u4 = getub(buf, 3);
917         GPSD_LOG(LOG_PROG, &session->context->errout,
918                  "TSIP: IO Options (0x55): %02x %02x %02x %02x\n",
919                  u1, u2, u3, u4);
920         if ((u1 & 0x20) != (uint8_t) 0) {
921             /* Try to get Super Packets */
922             /* Turn off 0x8f-20 LFwEI Super Packet */
923             putbyte(buf, 0, 0x20);
924             putbyte(buf, 1, 0x00);      /* disabled */
925             (void)tsip_write(session, 0x8e, buf, 2);
926 
927             /* Turn on Compact Super Packet 0x8f-23 */
928             putbyte(buf, 0, 0x23);
929             putbyte(buf, 1, 0x01);      /* enabled */
930             (void)tsip_write(session, 0x8e, buf, 2);
931             session->driver.tsip.req_compact = now;
932         }
933         break;
934     case 0x56:
935         /* Velocity Fix, East-North-Up (ENU)
936          * Present in:
937          *   pre-2000 models
938          *   Copernicus II (2009)
939          *   ICM SMT 360 (2018)
940          *   RES SMT 360 (2018)
941          */
942         if (len != 20) {
943             bad_len = 20;
944             break;
945         }
946         f1 = getbef32((char *)buf, 0);  /* East velocity */
947         f2 = getbef32((char *)buf, 4);  /* North velocity */
948         f3 = getbef32((char *)buf, 8);  /* Up velocity */
949         f4 = getbef32((char *)buf, 12); /* clock bias rate */
950         ftow = getbef32((char *)buf, 16); /* time-of-fix */
951         DTOTS(&ts_tow, ftow);
952         session->newdata.time = gpsd_gpstime_resolv(session,
953                                                     session->context->gps_week,
954                                                     ts_tow);
955         session->newdata.NED.velN = f2;
956         session->newdata.NED.velE = f1;
957         session->newdata.NED.velD = -f3;
958         mask |= VNED_SET | TIME_SET | NTPTIME_IS;
959         if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
960             mask |= CLEAR_IS;
961             session->driver.tsip.last_tow = ts_tow;
962         }
963         GPSD_LOG(LOG_PROG, &session->context->errout,
964                  "TSIP: Vel ENU (0x56): %f %f %f %f ftow %f\n",
965                  f1, f2, f3, f4, ftow);
966         break;
967     case 0x57:
968         /* Information About Last Computed Fix
969          * Present in:
970          *   pre-2000 models
971          *   Copernicus II (2009)
972          *   ICM SMT 360 (2018)
973          *   RES SMT 360 (2018)
974          */
975         if (len != 8) {
976             bad_len = 8;
977             break;
978         }
979         u1 = getub(buf, 0);                     /* Source of information */
980         u2 = getub(buf, 1);                     /* Mfg. diagnostic */
981         ftow = getbef32((char *)buf, 2);        /* gps_time */
982         week = getbeu16(buf, 6);                /* tsip.gps_week */
983         if (getub(buf, 0) == 0x01) {
984             /* good current fix */
985             DTOTS(&ts_tow, ftow);
986             (void)gpsd_gpstime_resolv(session, week, ts_tow);
987             mask |= TIME_SET | NTPTIME_IS;
988             if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
989                 mask |= CLEAR_IS;
990                 session->driver.tsip.last_tow = ts_tow;
991             }
992         }
993         GPSD_LOG(LOG_PROG, &session->context->errout,
994                  "TSIP: Fix info (0x57): %02x %02x %u %f\n", u1, u2, week, f1);
995         break;
996     case 0x5a:
997         /* Raw Measurement Data
998          * Present in:
999          *   pre-2000 models
1000          *   Copernicus II (2009)
1001          *   ICM SMT 360 (2018)
1002          *   RES SMT 360 (2018)
1003          */
1004         if (25 > len) {
1005             bad_len = 25;
1006             break;
1007         }
1008         // Useless without the pseudorange...
1009         u1 = getub(buf, 0);             // PRN 1-237
1010         f1 = getbef32((char *)buf, 1);  // sample length
1011         f2 = getbef32((char *)buf, 5);  // Signal Level, dbHz
1012         f3 = getbef32((char *)buf, 9);  // Code phase, 1/16th chip
1013         f4 = getbef32((char *)buf, 13); // Doppler, Hz @ L1
1014         d1 = getbed64((char *)buf, 17); // Time of Measurement
1015         GPSD_LOG(LOG_PROG, &session->context->errout,
1016                  "TSIP: Raw Measurement Data (0x5a): %d %f %f %f %f %f\n",
1017                  u1, f1, f2, f3, f4, d1);
1018         break;
1019     case 0x5c:
1020         /* Satellite Tracking Status (0x5c) polled by 0x3c
1021          *
1022          * GPS only, no WAAS reported here or used in fix
1023          * Present in:
1024          *   pre-2000 models
1025          *   Copernicus, Copernicus II
1026          *   Thunderbold E
1027          * Not Present in:
1028          *   ICM SMT 360 (2018)
1029          *   RES SMT 360 (2018)
1030          */
1031         if (len != 24) {
1032             bad_len = 24;
1033             break;
1034         }
1035         u1 = getub(buf, 0);     /* PRN 1-32 */
1036         u2 = getub(buf, 1);     /* slot:chan */
1037         u3 = getub(buf, 2);     /* Acquisition flag */
1038         u4 = getub(buf, 3);     /* Ephemeris flag */
1039         f1 = getbef32((char *)buf, 4);  /* Signal level */
1040         ftow = getbef32((char *)buf, 8);  /* time of Last measurement */
1041         d1 = getbef32((char *)buf, 12) * RAD_2_DEG;     /* Elevation */
1042         d2 = getbef32((char *)buf, 16) * RAD_2_DEG;     /* Azimuth */
1043 
1044         /* Channel number, bits 0-2 reserved/unused as of 1999.
1045          * Seems to always start series at zero and increment to last one.
1046          * No way to know how many there will be.
1047          * Save current channel to check for last 0x5c message
1048          */
1049         i = (int)(u2 >> 3);     /* channel number */
1050         if (0 == i) {
1051             // start of new cycle, save last count
1052             session->gpsdata.satellites_visible =
1053                 session->driver.tsip.last_chan_seen;
1054         }
1055         session->driver.tsip.last_chan_seen = i;
1056 
1057         GPSD_LOG(LOG_PROG, &session->context->errout,
1058                  "TSIP: Satellite Tracking Status (0x5c): Ch %2d PRN %3d "
1059                  "es %d Acq %d Eph %2d SNR %4.1f LMT %.04f El %4.1f Az %5.1f\n",
1060                  i, u1, u2 & 7, u3, u4, f1, ftow, d1, d2);
1061         if (i < TSIP_CHANNELS) {
1062             session->gpsdata.skyview[i].PRN = (short)u1;
1063             session->gpsdata.skyview[i].svid = (unsigned char)u1;
1064             session->gpsdata.skyview[i].gnssid = GNSSID_GPS;
1065             session->gpsdata.skyview[i].ss = (double)f1;
1066             session->gpsdata.skyview[i].elevation = (double)d1;
1067             session->gpsdata.skyview[i].azimuth = (double)d2;
1068             session->gpsdata.skyview[i].used = false;
1069             session->gpsdata.skyview[i].gnssid = tsip_gnssid(0, u1,
1070                 &session->gpsdata.skyview[i].svid);
1071             if (0.1 < f1) {
1072                 // check used list, if ss is non-zero
1073                 for (j = 0; j < session->gpsdata.satellites_used; j++) {
1074                     if (session->gpsdata.skyview[i].PRN != 0 &&
1075                         session->driver.tsip.sats_used[j] != 0) {
1076                         session->gpsdata.skyview[i].used = true;
1077                     }
1078                 }
1079             }
1080             /* when polled by 0x3c, all the skyview times will be the same
1081              * in one cluster */
1082             if (0.0 < ftow) {
1083                 DTOTS(&ts_tow, ftow);
1084                 session->gpsdata.skyview_time =
1085                     gpsd_gpstime_resolv(session, session->context->gps_week,
1086                                         ts_tow);
1087                 /* do not save in session->driver.tsip.last_tow
1088                  * as this is skyview time, not fix time */
1089             }
1090             if (++i >= session->gpsdata.satellites_visible) {
1091                 /* Last of the series?
1092                  * This will cause extra SKY if this set has more
1093                  * sats than the last set */
1094                 mask |= SATELLITE_SET;
1095                 session->gpsdata.satellites_visible = i;
1096             }
1097             /* If this series has fewer than last series there will
1098              * be no SKY, unless the cycle ender pushes the SKY */
1099         }
1100         break;
1101 
1102      case 0x5d:
1103         /* GNSS Satellite Tracking Status (multi-GNSS operation) (0x5d)
1104          * polled by 0x3c
1105          *
1106          * GNSS only, no WAAS reported here or used in fix
1107          * Present in:
1108          *   ICM SMT 360 (2018)
1109          *   RES SMT 360 (2018)
1110          * Not Present in:
1111          *   pre-2000 models
1112          *   Copernicus, Copernicus II
1113          *   Thunderbold E
1114          */
1115         if (len != 26) {
1116             bad_len = 26;
1117             break;
1118         }
1119         u1 = getub(buf, 0);     /* PRN */
1120 
1121         /* Channel number, bits 0-2 reserved/unused as of 1999.
1122          * Seems to always start series at zero and increment to last one.
1123          * No way to know how many there will be.
1124          * Save current channel to check for last 0x5d message
1125          */
1126         i = getub(buf, 1);     /* chan */
1127         if (0 == i) {
1128             // start of new cycle, save last count
1129             session->gpsdata.satellites_visible =
1130                 session->driver.tsip.last_chan_seen;
1131         }
1132         session->driver.tsip.last_chan_seen = i;
1133 
1134         u3 = getub(buf, 2);     /* Acquisition flag */
1135         u4 = getub(buf, 3);     /* SV used in Position or Time calculation*/
1136         f1 = getbef32((char *)buf, 4);  /* Signal level */
1137         // This can be one second behind the TPV on RES SMT 360
1138         ftow = getbef32((char *)buf, 8);  /* time of Last measurement */
1139         d1 = getbef32((char *)buf, 12) * RAD_2_DEG;     /* Elevation */
1140         d2 = getbef32((char *)buf, 16) * RAD_2_DEG;     /* Azimuth */
1141         u5 = getub(buf, 20);    /* old measurement flag */
1142         u6 = getub(buf, 21);    /* integer msec flag */
1143         u7 = getub(buf, 22);    /* bad data flag */
1144         u8 = getub(buf, 23);    /* data collection flag */
1145         u9 = getub(buf, 24);    /* Used flags */
1146         u10 = getub(buf, 25);   /* SV Type */
1147 
1148 
1149         GPSD_LOG(LOG_PROG, &session->context->errout,
1150                 "TSIP: Satellite Tracking Status (0x5d): Ch %2d Con %d PRN %3d "
1151                 "Acq %d Use %d SNR %4.1f LMT %.04f El %4.1f Az %5.1f Old %d "
1152                 "Int %d Bad %d Col %d TPF %d SVT %d\n",
1153                 i, u10, u1, u3, u4, f1, ftow, d1, d2, u5, u6, u7, u8, u9, u10);
1154         if (i < TSIP_CHANNELS) {
1155             session->gpsdata.skyview[i].PRN = (short)u1;
1156             session->gpsdata.skyview[i].ss = (double)f1;
1157             session->gpsdata.skyview[i].elevation = (double)d1;
1158             session->gpsdata.skyview[i].azimuth = (double)d2;
1159             session->gpsdata.skyview[i].used = (bool)u4;
1160             session->gpsdata.skyview[i].gnssid = tsip_gnssid(u10, u1,
1161                 &session->gpsdata.skyview[i].svid);
1162             if (0 == u7) {
1163 		session->gpsdata.skyview[i].health = SAT_HEALTH_OK;
1164             } else {
1165 		session->gpsdata.skyview[i].health = SAT_HEALTH_BAD;
1166             }
1167 
1168             /* when polled by 0x3c, all the skyview times will be the same
1169              * in one cluster */
1170             if (0.0 < ftow) {
1171                 DTOTS(&ts_tow, ftow);
1172                 session->gpsdata.skyview_time =
1173                     gpsd_gpstime_resolv(session, session->context->gps_week,
1174                                         ts_tow);
1175                 /* do not save in session->driver.tsip.last_tow
1176                  * as this is skyview time, not fix time */
1177             }
1178             if (++i >= session->gpsdata.satellites_visible) {
1179                 /* Last of the series?
1180                  * This will cause extra SKY if this set has more
1181                  * sats than the last set */
1182                 mask |= SATELLITE_SET;
1183                 session->gpsdata.satellites_visible = i;
1184             }
1185             /* If this series has fewer than last series there will
1186              * be no SKY, unless the cycle ender pushes the SKY */
1187         }
1188         break;
1189     case 0x6c:
1190         /* Satellite Selection List (0x6c) polled by 0x24
1191          *
1192          * Present in:
1193          *   ICM SMT 360 (2018)
1194          *   RES SMT 360 (2018)
1195          * Not present in:
1196          *   pre-2000 models
1197          *   Copernicus II (2009)
1198          *   Lassen SQ (2002)
1199          *   Lassen iQ (2005) */
1200         if (18 > len) {
1201             bad_len = 18;
1202             break;
1203         }
1204         u1 = getub(buf, 0);          // fix dimension, mode
1205         count = (int)getub(buf, 17);
1206         if (len != (18 + count)) {
1207             bad_len = 18 + count;
1208             break;
1209         }
1210 
1211         // why same as 6d?
1212         session->driver.tsip.last_6d = now;     /* keep timestamp for request */
1213         /*
1214          * This looks right, but it sets a spurious mode value when
1215          * the satellite constellation looks good to the chip but no
1216          * actual fix has yet been acquired.  We should set the mode
1217          * field (which controls gpsd's fix reporting) only from sentences
1218          * that convey actual fix information, like 0x8f-20, but some
1219          * TSIP do not support 0x8f-20, and 0x6c may be all we got.
1220          */
1221         switch (u1 & 7) {       /* dimension */
1222         case 1:       // clock fix (surveyed in)
1223             // FALLTHROUGH
1224         case 5:       // Overdetermined clock fix
1225             session->gpsdata.status = STATUS_TIME;
1226             session->newdata.mode = MODE_3D;
1227             break;
1228         case 3:
1229             session->gpsdata.status = STATUS_FIX;
1230             session->newdata.mode = MODE_2D;
1231             break;
1232         case 4:
1233             session->gpsdata.status = STATUS_FIX;
1234             session->newdata.mode = MODE_3D;
1235             break;
1236         case 2:
1237             // FALLTHROUGH
1238         case 6:
1239             // FALLTHROUGH
1240         case 7:
1241             // FALLTHROUGH
1242         default:
1243             session->gpsdata.status = STATUS_NO_FIX;
1244             session->newdata.mode = MODE_NO_FIX;
1245             break;
1246         }
1247         if (8 == (u1 & 8)) {
1248             // Surveyed in
1249             session->gpsdata.status = STATUS_TIME;
1250         }
1251         mask |= MODE_SET;
1252 
1253         session->gpsdata.satellites_used = count;
1254         session->gpsdata.dop.pdop = getbef32((char *)buf, 1);
1255         session->gpsdata.dop.hdop = getbef32((char *)buf, 5);
1256         session->gpsdata.dop.vdop = getbef32((char *)buf, 9);
1257         // RES SMT 360 and ICM SMT 360 always report tdop == 1
1258         session->gpsdata.dop.tdop = getbef32((char *)buf, 13);
1259         session->gpsdata.dop.gdop =
1260             sqrt(pow(session->gpsdata.dop.pdop, 2) +
1261                  pow(session->gpsdata.dop.tdop, 2));
1262 
1263         memset(session->driver.tsip.sats_used, 0,
1264                 sizeof(session->driver.tsip.sats_used));
1265         buf2[0] = '\0';
1266         for (i = 0; i < count; i++) {
1267             session->driver.tsip.sats_used[i] = (short)getub(buf, 18 + i);
1268             if (session->context->errout.debug >= LOG_DATA) {
1269                 str_appendf(buf2, sizeof(buf2),
1270                                " %d", session->driver.tsip.sats_used[i]);
1271             }
1272         }
1273         GPSD_LOG(LOG_PROG, &session->context->errout,
1274                  "TSIP: AIVSS (0x6c): mode %d status %d used %d "
1275                  "pdop %.1f hdop %.1f vdop %.1f tdop %.1f gdop %.1f Used %s\n",
1276                  session->newdata.mode,
1277                  session->gpsdata.status,
1278                  session->gpsdata.satellites_used,
1279                  session->gpsdata.dop.pdop,
1280                  session->gpsdata.dop.hdop,
1281                  session->gpsdata.dop.vdop,
1282                  session->gpsdata.dop.tdop,
1283                  session->gpsdata.dop.gdop,
1284                  buf2);
1285         mask |= DOP_SET | MODE_SET | STATUS_SET | USED_IS;
1286         break;
1287     case 0x6d:
1288         /* All-In-View Satellite Selection (0x6d) polled by 0x24
1289          *
1290          * Present in:
1291          *   pre-2000 models
1292          *   Copernicus II (2009)
1293          *   Lassen SQ
1294          *   Lassen iQ
1295          * Not present in:
1296          *   ICM SMT 360 (2018)
1297          *   RES SMT 360 (2018)
1298          */
1299         if (1 > len) {
1300             bad_len = 1;
1301             break;
1302         }
1303         u1 = getub(buf, 0);     /* nsvs/dimension */
1304         count = (int)((u1 >> 4) & 0x0f);
1305         if (len != (17 + count)) {
1306             bad_len = 17 + count;
1307             break;
1308         }
1309         session->driver.tsip.last_6d = now;     /* keep timestamp for request */
1310         /*
1311          * This looks right, but it sets a spurious mode value when
1312          * the satellite constellation looks good to the chip but no
1313          * actual fix has yet been acquired.  We should set the mode
1314          * field (which controls gpsd's fix reporting) only from sentences
1315          * that convey actual fix information, like 0x8f-20, but some
1316          * TSIP do not support 0x8f-20, and 0x6c may be all we got.
1317          */
1318         if (0 != isfinite(session->gpsdata.fix.longitude)) {
1319             // have a fix
1320             switch (u1 & 7) {   /* dimension */
1321             case 1:       // clock fix (surveyed in)
1322                 // FALLTHROUGH
1323             case 5:       // Overdetermined clock fix
1324                 session->gpsdata.status = STATUS_TIME;
1325                 session->newdata.mode = MODE_3D;
1326                 break;
1327             case 3:
1328                 session->gpsdata.status = STATUS_FIX;
1329                 session->newdata.mode = MODE_2D;
1330                 break;
1331             case 4:
1332                 session->gpsdata.status = STATUS_FIX;
1333                 session->newdata.mode = MODE_3D;
1334                 break;
1335             case 2:
1336                 // FALLTHROUGH
1337             case 6:
1338                 // FALLTHROUGH
1339             case 7:
1340                 // FALLTHROUGH
1341             default:
1342                 session->gpsdata.status = STATUS_NO_FIX;
1343                 session->newdata.mode = MODE_NO_FIX;
1344                 break;
1345             }
1346         } else {
1347             session->gpsdata.status = STATUS_NO_FIX;
1348             session->newdata.mode = MODE_NO_FIX;
1349         }
1350         mask |= MODE_SET;
1351 
1352         session->gpsdata.satellites_used = count;
1353         session->gpsdata.dop.pdop = getbef32((char *)buf, 1);
1354         session->gpsdata.dop.hdop = getbef32((char *)buf, 5);
1355         session->gpsdata.dop.vdop = getbef32((char *)buf, 9);
1356         session->gpsdata.dop.tdop = getbef32((char *)buf, 13);
1357         session->gpsdata.dop.gdop =
1358             sqrt(pow(session->gpsdata.dop.pdop, 2) +
1359                  pow(session->gpsdata.dop.tdop, 2));
1360 
1361         memset(session->driver.tsip.sats_used, 0,
1362                sizeof(session->driver.tsip.sats_used));
1363         buf2[0] = '\0';
1364         for (i = 0; i < count; i++) {
1365             // negative PRN means sat unhealthy
1366             session->driver.tsip.sats_used[i] = (short)getub(buf, 17 + i);
1367             if (session->context->errout.debug >= LOG_DATA) {
1368                 str_appendf(buf2, sizeof(buf2),
1369                                " %d", session->driver.tsip.sats_used[i]);
1370             }
1371         }
1372         GPSD_LOG(LOG_PROG, &session->context->errout,
1373                  "TSIP: AIVSS (0x6d) status=%d used=%d "
1374                  "pdop=%.1f hdop=%.1f vdop=%.1f tdop=%.1f gdop=%.1f used:%s\n",
1375                  session->gpsdata.status,
1376                  session->gpsdata.satellites_used,
1377                  session->gpsdata.dop.pdop,
1378                  session->gpsdata.dop.hdop,
1379                  session->gpsdata.dop.vdop,
1380                  session->gpsdata.dop.tdop,
1381                  session->gpsdata.dop.gdop, buf2);
1382         mask |= DOP_SET | STATUS_SET | USED_IS;
1383         break;
1384     case 0x82:
1385         /* Differential Position Fix Mode (0x82) poll with 0x62-ff
1386          * Sent after every position fix in Auto GPS/DGPS,
1387          * so potential cycle ender
1388          *
1389          * Present in:
1390          *   pre-2000 models
1391          *   Copernicus II (2009)
1392          *   Lassen iQ, deprecated use 0xbb instead
1393          * Not Present in:
1394          *   ICM SMT 360 (2018)
1395          *   RES SMT 360 (2018)
1396          */
1397         if (len != 1) {
1398             bad_len = 1;
1399             break;
1400         }
1401         u1 = getub(buf, 0);     /* fix mode */
1402         if (session->gpsdata.status == STATUS_FIX && (u1 & 0x01) != 0) {
1403             session->gpsdata.status = STATUS_DGPS_FIX;
1404             mask |= STATUS_SET;
1405         }
1406         GPSD_LOG(LOG_PROG, &session->context->errout,
1407                  "TSIP: DPFM (0x82) mode %d status=%d\n",
1408                  u1, session->gpsdata.status);
1409         break;
1410     case 0x83:
1411         /* Double-Precision XYZ Position Fix and Bias Information
1412          * Present in:
1413          *   pre-2000 models
1414          *   Copernicus II (2009)
1415          *   ICM SMT 360 (2018)
1416          *   RES SMT 360 (2018)
1417          */
1418         if (36 > len) {
1419             bad_len = 36;
1420             break;
1421         }
1422         session->newdata.ecef.x = getbed64((char *)buf, 0);  /* X */
1423         session->newdata.ecef.y = getbed64((char *)buf, 8);  /* Y */
1424         session->newdata.ecef.z = getbed64((char *)buf, 16); /* Z */
1425         d4 = getbed64((char *)buf, 24); /* clock bias */
1426         ftow = getbef32((char *)buf, 32); /* time-of-fix */
1427         DTOTS(&ts_tow, ftow);
1428         session->newdata.time = gpsd_gpstime_resolv(session,
1429                                                     session->context->gps_week,
1430                                                     ts_tow);
1431         GPSD_LOG(LOG_PROG, &session->context->errout,
1432                  "TSIP: DP-XYZ (0x83) %f %f %f %f tow %f\n",
1433                  session->newdata.ecef.x,
1434                  session->newdata.ecef.y,
1435                  session->newdata.ecef.z,
1436                  d4, ftow);
1437         mask = ECEF_SET | TIME_SET | NTPTIME_IS;
1438         if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
1439             mask |= CLEAR_IS;
1440             session->driver.tsip.last_tow = ts_tow;
1441         }
1442         break;
1443     case 0x84:
1444         /* Double-Precision LLA Position Fix and Bias Information
1445          * Present in:
1446          *   pre-2000 models
1447          *   Copernicus II (2009)
1448          *   ICM SMT 360 (2018)
1449          *   RES SMT 360 (2018)
1450          */
1451         if (len != 36) {
1452             bad_len = 36;
1453             break;
1454         }
1455         session->newdata.latitude = getbed64((char *)buf, 0) * RAD_2_DEG;
1456         session->newdata.longitude = getbed64((char *)buf, 8) * RAD_2_DEG;
1457         /* depending on GPS config, could be either WGS84 or MSL */
1458         d1 = getbed64((char *)buf, 16);
1459         if (0 == session->driver.tsip.alt_is_msl) {
1460             session->newdata.altHAE = d1;
1461         } else {
1462             session->newdata.altMSL = d1;
1463         }
1464         mask |= ALTITUDE_SET;
1465         //d1 = getbed64((char *)buf, 24);       clock bias */
1466         ftow = getbef32((char *)buf, 32);       /* time-of-fix */
1467         if ((session->context->valid & GPS_TIME_VALID)!=0) {
1468             DTOTS(&ts_tow, ftow);
1469             session->newdata.time =
1470                 gpsd_gpstime_resolv(session, session->context->gps_week,
1471                                     ts_tow);
1472             mask |= TIME_SET | NTPTIME_IS;
1473             if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
1474                 mask |= CLEAR_IS;
1475                 session->driver.tsip.last_tow = ts_tow;
1476             }
1477         }
1478         mask |= LATLON_SET;
1479         GPSD_LOG(LOG_PROG, &session->context->errout,
1480                  "TSIP: DP-LLA (0x84) time=%s lat=%.2f lon=%.2f alt=%.2f\n",
1481                  timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1482                  session->newdata.latitude,
1483                  session->newdata.longitude, d1);
1484         break;
1485     case 0x8f:
1486         /* Super Packet.
1487          * Present in:
1488          *   pre-2000 models
1489          *   ACE II
1490          *   ACE III
1491          *   Copernicus II (2009)
1492          *   ICM SMT 360
1493          *   RES SMT 360
1494          */
1495         u1 = (uint8_t) getub(buf, 0);
1496         switch (u1) {           /* sub-code ID */
1497         case 0x15:
1498             /* Current Datum Values
1499              * Not Present in:
1500              *   pre-2000 models
1501              *   Copernicus II (2009)
1502              *   ICM SMT 360 (2018)
1503              *   RES SMT 360 (2018)
1504              */
1505             if (len != 43) {
1506                 bad_len = 43;
1507                 break;
1508             }
1509             s1 = getbes16(buf, 1);      /* Datum Index */
1510             d1 = getbed64((char *)buf, 3);      /* DX */
1511             d2 = getbed64((char *)buf, 11);     /* DY */
1512             d3 = getbed64((char *)buf, 19);     /* DZ */
1513             d4 = getbed64((char *)buf, 27);     /* A-axis */
1514             d5 = getbed64((char *)buf, 35);     /* Eccentricity Squared */
1515             GPSD_LOG(LOG_PROG, &session->context->errout,
1516                      "TSIP: Current Datum (0x8f-15) %d %f %f %f %f %f\n",
1517                      s1, d1, d2, d3, d4, d5);
1518             break;
1519 
1520         case 0x20:
1521             /* Last Fix with Extra Information (binary fixed point) 0x8f-20
1522              * Only ouput when fix is available.
1523              * CSK sez "why does my Lassen SQ output oversize packets?"
1524              * Present in:
1525              *   pre-2000 models
1526              *   ACE II
1527              *   Copernicus, Copernicus II (64-bytes)
1528              * Not present in:
1529              *   ICM SMT 360
1530              *   RES SMT 360
1531              */
1532             if ((len != 56) && (len != 64)) {
1533                 bad_len = 56;
1534                 break;
1535             }
1536             s1 = getbes16(buf, 2);      /* east velocity */
1537             s2 = getbes16(buf, 4);      /* north velocity */
1538             s3 = getbes16(buf, 6);      /* up velocity */
1539             tow = getbeu32(buf, 8);     /* time in ms */
1540             sl1 = getbes32(buf, 12);    /* latitude */
1541             ul2 = getbeu32(buf, 16);    /* longitude */
1542             // Lassen iQ, and copernicus (ii) doc says this is always altHAE
1543             sl2 = getbes32(buf, 20);    /* altitude */
1544             u1 = getub(buf, 24);        /* velocity scaling */
1545             u2 = getub(buf, 27);        /* fix flags */
1546             u3 = getub(buf, 28);        /* num svs */
1547             u4 = getub(buf, 29);        /* utc offset */
1548             week = getbeu16(buf, 30);   /* tsip.gps_week */
1549             /* PRN/IODE data follows */
1550             GPSD_LOG(LOG_PROG, &session->context->errout,
1551                      "TSIP: LFwEI (0x8f-20) %d %d %d tow %u %d "
1552                      " %u %u %x %x %u leap %u week %d\n",
1553                      s1, s2, s3, tow, sl1, ul2, sl2, u1, u2, u3, u4, week);
1554 
1555             if ((u1 & 0x01) != (uint8_t) 0)     /* check velocity scaling */
1556                 d5 = 0.02;
1557             else
1558                 d5 = 0.005;
1559 
1560             // 0x8000 is over-range
1561             if ((int16_t)0x8000 != s2) {
1562                 d2 = (double)s2 * d5;   /* north velocity m/s */
1563                 session->newdata.NED.velN = d2;
1564             }
1565             if ((int16_t)0x8000 != s1) {
1566                 d1 = (double)s1 * d5;   /* east velocity m/s */
1567                 session->newdata.NED.velE = d1;
1568             }
1569             if ((int16_t)0x8000 != s3) {
1570                 d3 = (double)s3 * d5;       /* up velocity m/s */
1571                 session->newdata.NED.velD = -d3;
1572             }
1573 
1574             session->newdata.latitude = (double)sl1 * SEMI_2_DEG;
1575             session->newdata.longitude = (double)ul2 * SEMI_2_DEG;
1576             if (session->newdata.longitude > 180.0)
1577                 session->newdata.longitude -= 360.0;
1578             // Lassen iQ doc says this is always altHAE in mm
1579             session->newdata.altHAE = (double)sl2 * 1e-3;
1580             mask |= ALTITUDE_SET;
1581 
1582             session->gpsdata.status = STATUS_NO_FIX;
1583             session->newdata.mode = MODE_NO_FIX;
1584             if ((u2 & 0x01) == (uint8_t) 0) {   /* Fix Available */
1585                 session->gpsdata.status = STATUS_FIX;
1586                 if ((u2 & 0x02) != (uint8_t) 0) /* DGPS Corrected */
1587                     session->gpsdata.status = STATUS_DGPS_FIX;
1588                 if ((u2 & 0x04) != (uint8_t) 0) /* Fix Dimension */
1589                     session->newdata.mode = MODE_2D;
1590                 else
1591                     session->newdata.mode = MODE_3D;
1592             }
1593             session->gpsdata.satellites_used = (int)u3;
1594             if ((int)u4 > 10) {
1595                 session->context->leap_seconds = (int)u4;
1596                 session->context->valid |= LEAP_SECOND_VALID;
1597                 /* check for week rollover
1598                  * Trimble uses 15 bit weeks, but can guess the epoch wrong
1599                  * Can not be in gpsd_gpstime_resolv() because that
1600                  * may see BUILD_LEAPSECONDS instead of leap_seconds
1601                  * from receiver.
1602                  */
1603                 if (17 < u4 && 1930 > week) {
1604                     // leap second 18 added in gps week 1930
1605                     week += 1024;
1606                     if (1930 > week) {
1607                         // and again?
1608                         week += 1024;
1609                     }
1610                 }
1611             }
1612             MSTOTS(&ts_tow, tow);
1613             session->newdata.time = gpsd_gpstime_resolv(session, week,
1614                                                         ts_tow);
1615             mask |= TIME_SET | NTPTIME_IS | LATLON_SET |
1616                     STATUS_SET | MODE_SET | VNED_SET;
1617             if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
1618                 mask |= CLEAR_IS;
1619                 session->driver.tsip.last_tow = ts_tow;
1620             }
1621             GPSD_LOG(LOG_DATA, &session->context->errout,
1622                      "TSIP: LFwEI (0x8f-20): time=%s lat=%.2f lon=%.2f "
1623                      "altHAE=%.2f mode=%d status=%d\n",
1624                      timespec_str(&session->newdata.time, ts_buf,
1625                                   sizeof(ts_buf)),
1626                      session->newdata.latitude, session->newdata.longitude,
1627                      session->newdata.altHAE,
1628                      session->newdata.mode, session->gpsdata.status);
1629             break;
1630         case 0x23:
1631             /* Compact Super Packet (0x8f-23)
1632              * Present in:
1633              *   Copernicus, Copernicus II
1634              * Not present in:
1635              *   pre-2000 models
1636              *   Lassen iQ
1637              *   ICM SMT 360
1638              *   RES SMT 360
1639              */
1640             session->driver.tsip.req_compact = 0;
1641             /* CSK sez "i don't trust this to not be oversized either." */
1642             if (29 > len) {
1643                 bad_len = 29;
1644                 break;
1645             }
1646             tow = getbeu32(buf, 1);             /* time in ms */
1647             week = getbeu16(buf, 5);            /* tsip.gps_week */
1648             u1 = getub(buf, 7);                 /* utc offset */
1649             u2 = getub(buf, 8);                 /* fix flags */
1650             sl1 = getbes32(buf, 9);             /* latitude */
1651             ul2 = getbeu32(buf, 13);            /* longitude */
1652             // Copernicus (ii) doc says this is always altHAE in mm
1653             sl3 = getbes32(buf, 17);    /* altitude */
1654             /* set xNED here */
1655             s2 = getbes16(buf, 21);     /* east velocity */
1656             s3 = getbes16(buf, 23);     /* north velocity */
1657             s4 = getbes16(buf, 25);     /* up velocity */
1658             GPSD_LOG(LOG_PROG, &session->context->errout,
1659                      "TSIP: CSP (0x8f-23): %u %d %u %u %d %u %d %d %d %d\n",
1660                      tow, week, u1, u2, sl1, ul2, sl3, s2, s3, s4);
1661             if ((int)u1 > 10) {
1662                 session->context->leap_seconds = (int)u1;
1663                 session->context->valid |= LEAP_SECOND_VALID;
1664             }
1665             MSTOTS(&ts_tow, tow);
1666             session->newdata.time =
1667                 gpsd_gpstime_resolv(session, week, ts_tow);
1668             session->gpsdata.status = STATUS_NO_FIX;
1669             session->newdata.mode = MODE_NO_FIX;
1670             if ((u2 & 0x01) == (uint8_t) 0) {   /* Fix Available */
1671                 session->gpsdata.status = STATUS_FIX;
1672                 if ((u2 & 0x02) != (uint8_t) 0) /* DGPS Corrected */
1673                     session->gpsdata.status = STATUS_DGPS_FIX;
1674                 if ((u2 & 0x04) != (uint8_t) 0) /* Fix Dimension */
1675                     session->newdata.mode = MODE_2D;
1676                 else
1677                     session->newdata.mode = MODE_3D;
1678             }
1679             session->newdata.latitude = (double)sl1 * SEMI_2_DEG;
1680             session->newdata.longitude = (double)ul2 * SEMI_2_DEG;
1681             if (session->newdata.longitude > 180.0)
1682                 session->newdata.longitude -= 360.0;
1683             // Copernicus (ii) doc says this is always altHAE in mm
1684             session->newdata.altHAE = (double)sl3 * 1e-3;
1685             mask |= ALTITUDE_SET;
1686             if ((u2 & 0x20) != (uint8_t) 0)     /* check velocity scaling */
1687                 d5 = 0.02;
1688             else
1689                 d5 = 0.005;
1690             d1 = (double)s2 * d5;       /* east velocity m/s */
1691             d2 = (double)s3 * d5;       /* north velocity m/s */
1692             d3 = (double)s4 * d5;       /* up velocity m/s */
1693             session->newdata.NED.velN = d2;
1694             session->newdata.NED.velE = d1;
1695             session->newdata.NED.velD = -d3;
1696 
1697             mask |= TIME_SET | NTPTIME_IS | LATLON_SET |
1698                     STATUS_SET | MODE_SET | VNED_SET;
1699             if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
1700                 mask |= CLEAR_IS;
1701                 session->driver.tsip.last_tow = ts_tow;
1702             }
1703             GPSD_LOG(LOG_PROG, &session->context->errout,
1704                      "TSIP: SP-CSP (0x8f-23): time %s lat %.2f lon %.2f "
1705                      "altHAE %.2f mode %d status %d\n",
1706                      timespec_str(&session->newdata.time, ts_buf,
1707                                   sizeof(ts_buf)),
1708                      session->newdata.latitude, session->newdata.longitude,
1709                      session->newdata.altHAE,
1710                      session->newdata.mode, session->gpsdata.status);
1711             break;
1712 
1713         case 0xa5:
1714             /* Packet Broadcast Mask (0x8f-a5) polled by 0x8e-a5
1715              *
1716              * Present in:
1717              *   ICM SMT 360
1718              *   RES SMT 360
1719              * Not Present in:
1720              *   pre-2000 models
1721              *   Copernicus II (2009)
1722              */
1723             {
1724                 uint16_t mask0, mask1;
1725                 if (5 > len) {
1726                     bad_len = 5;
1727                     break;
1728                 }
1729                 mask0 = getbeu16(buf, 1);    // Mask 0
1730                 mask1 = getbeu16(buf, 3);    // Mask 1
1731                 GPSD_LOG(LOG_DATA, &session->context->errout,
1732                          "TSIP: PBM (0x8f-a5) mask0 x%04x mask1 x%04x\n",
1733                          mask0, mask1);
1734             }
1735             // RES SMT 360 default 5, 0
1736             break;
1737 
1738         case 0xa6:
1739             /* Self-Survey Comman (0x8f-a6) polled by 0x8e-a6
1740              *
1741              * Present in:
1742              *   ICM SMT 360
1743              *   RES SMT 360
1744              * Not Present in:
1745              *   pre-2000 models
1746              *   Copernicus II (2009)
1747              */
1748             if (3 > len) {
1749                 bad_len = 3;
1750                 break;
1751             }
1752             u2 = getub(buf, 1);          // Command
1753             u3 = getub(buf, 2);          // Status
1754             GPSD_LOG(LOG_DATA, &session->context->errout,
1755                      "TSIP: SSC (0x8f-a6) command x%x status x%x\n",
1756                      u2, u3);
1757             break;
1758 
1759         case 0xab:
1760             /* Thunderbolt Timing Superpacket
1761              * Not Present in:
1762              *   pre-2000 models
1763              *   Copernicus II (2009)
1764              */
1765             if (17 > len) {
1766                 bad_len = 17;
1767                 break;
1768             }
1769             session->driver.tsip.last_41 = now; // keep timestamp for request
1770             // we assume the receiver not in some crazy mode, and is GPS time
1771             tow = getbeu32(buf, 1);             // gpstime in seconds
1772             ts_tow.tv_sec = tow;
1773             ts_tow.tv_nsec = 0;
1774             week = getbeu16(buf, 5);            // week
1775             /* leap seconds */
1776             session->context->leap_seconds = (int)getbes16(buf, 7);
1777             u2 = buf[9];                // Time Flag
1778             // should check time valid?
1779             /* ignore the broken down time, use the GNSS time.
1780              * Hope it is not BeiDou time */
1781 
1782             // how do we know leap valid?
1783             session->context->valid |= LEAP_SECOND_VALID;
1784             session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
1785             mask |= TIME_SET | NTPTIME_IS;
1786             if (!TS_EQ(&ts_tow, &session->driver.tsip.last_tow)) {
1787                 mask |= CLEAR_IS;
1788                 session->driver.tsip.last_tow = ts_tow;
1789             }
1790 
1791             GPSD_LOG(LOG_PROG, &session->context->errout,
1792                      "TSIP: SP-TTS (0x8f-ab) tow %u wk %u ls %d flag x%x "
1793                      "time %s mask %s\n",
1794                      tow, week, session->context->leap_seconds, u2,
1795                      timespec_str(&session->newdata.time, ts_buf,
1796                                   sizeof(ts_buf)),
1797                      gps_maskdump(mask));
1798             break;
1799 
1800 
1801         case 0xac:
1802             /* Supplemental Timing Packet (0x8f-ac)
1803              * present in:
1804              *   ThunderboltE
1805              *   ICM SMT 360
1806              *   RES SMT 360
1807              * Not Present in:
1808              *   pre-2000 models
1809              *   Lassen iQ
1810              *   Copernicus II (2009)
1811              */
1812             if (len != 68) {
1813                 bad_len = 68;
1814                 break;
1815             }
1816 
1817             // byte 0 is Subpacket ID
1818             u2 = getub(buf, 1);         /* Receiver Mode */
1819             u3 = getub(buf, 12);        /* GNSS Decoding Status */
1820             // ignore 2, Disciplining Mode
1821             // ignore 3, Self-Survey Progress
1822             // ignore 4-7, Holdover Duration
1823             // ignore 8-9, Critical Alarms
1824             // ignore 10-11, Minor Alarms
1825             // ignore 12, GNSS Decoding Status
1826             // ignore 13, Disciplining Activity
1827             // ignore 14, PPS indication
1828             // ignore 15, PPS reference
1829             /* PPS Offset in ns
1830              * save as (long)pico seconds
1831              * can't really use it as it is not referenced to any PPS */
1832             fqErr = getbef32((char *)buf, 16);
1833             session->gpsdata.qErr = (long)(fqErr * 1000);
1834             // ignore 20-23, Clock Offset
1835             // ignore 24-27, DAC Value
1836             // ignore 28-31, DAC Voltage
1837             // 32-35, Temperature degrees C
1838             temp = getbef32((char *)buf, 32);
1839             session->newdata.latitude = getbed64((char *)buf, 36) * RAD_2_DEG;
1840             session->newdata.longitude = getbed64((char *)buf, 44) * RAD_2_DEG;
1841             // SMT 360 doc says this is always altHAE in meters
1842             session->newdata.altHAE = getbed64((char *)buf, 52);
1843             // ignore 60-63, always zero
1844             // ignore 64-67, reserved
1845 
1846             if (u3 != (uint8_t)0) {
1847                 // not exactly true, could be sort of Dead Reckoning
1848                 session->gpsdata.status = STATUS_NO_FIX;
1849                 mask |= STATUS_SET;
1850             } else {
1851                 if (session->gpsdata.status < STATUS_FIX) {
1852                     session->gpsdata.status = STATUS_FIX;
1853                     mask |= STATUS_SET;
1854                 }
1855             }
1856 
1857             /* Decode Fix modes */
1858             switch (u2 & 7) {
1859             case 0:     /* Auto */
1860                 /*
1861                 * According to the Thunderbolt Manual, the
1862                 * first byte of the supplemental timing packet
1863                 * simply indicates the configuration of the
1864                 * device, not the actual lock, so we need to
1865                 * look at the decode status.
1866                 */
1867                 switch (u3) {
1868                 case 0:   /* "Doing Fixes" */
1869                     session->newdata.mode = MODE_3D;
1870                     break;
1871                 case 0x0B: /* "Only 3 usable sats" */
1872                     session->newdata.mode = MODE_2D;
1873                     break;
1874                 case 0x1:   /* "Don't have GPS time" */
1875                     // FALLTHROUGH
1876                 case 0x3:   /* "PDOP is too high" */
1877                     // FALLTHROUGH
1878                 case 0x8:   /* "No usable sats" */
1879                     // FALLTHROUGH
1880                 case 0x9:   /* "Only 1 usable sat" */
1881                     // FALLTHROUGH
1882                 case 0x0A:  /* "Only 2 usable sats */
1883                     // FALLTHROUGH
1884                 case 0x0C:  /* "The chosen sat is unusable" */
1885                     // FALLTHROUGH
1886                 case 0x10:  /* TRAIM rejected the fix */
1887                     // FALLTHROUGH
1888                 default:
1889                     session->newdata.mode = MODE_NO_FIX;
1890                     break;
1891                 }
1892                 break;
1893             case 6:             /* Clock Hold 2D */
1894                 // FALLTHROUGH
1895             case 3:             /* 2D Position Fix */
1896                 //session->gpsdata.status = STATUS_FIX;
1897                 session->newdata.mode = MODE_2D;
1898                 break;
1899             case 7:             /* Thunderbolt overdetermined clock */
1900                 // FALLTHROUGH
1901             case 4:             /* 3D position Fix */
1902                 //session->gpsdata.status = STATUS_FIX;
1903                 session->newdata.mode = MODE_3D;
1904                 break;
1905             default:
1906                 //session->gpsdata.status = STATUS_NO_FIX;
1907                 session->newdata.mode = MODE_NO_FIX;
1908                 break;
1909             }
1910 
1911             mask |= LATLON_SET | ALTITUDE_SET | MODE_SET;
1912             GPSD_LOG(LOG_PROG, &session->context->errout,
1913                      "TSIP: SP-TPS (0x8f-ac) lat=%.2f lon=%.2f altHAE=%.2f "
1914                      "mode %d temp %.1f fqErr %.4f\n",
1915                      session->newdata.latitude,
1916                      session->newdata.longitude,
1917                      session->newdata.altHAE,
1918                      session->newdata.mode,
1919                      temp, fqErr);
1920             break;
1921 
1922         case 0x02:
1923             /* UTC Information
1924              * Present in:
1925              *   ICM SMT 360 (2018)
1926              *   RES SMT 360 (2018)
1927              * Not Present in:
1928              *   pre-2000 models
1929              *   Copernicus II (2009)
1930              */
1931             // FALLTHROUGH
1932         case 0x21:
1933             /* Request accuracy information
1934              * Present in:
1935              *   Copernicus II (2009)
1936              * Not Present in:
1937              *   pre-2000 models
1938              */
1939             // FALLTHROUGH
1940         case 0x2a:
1941             /* Request Fix and Channel Tracking info, Type 1
1942              * Present in:
1943              *   Copernicus II (2009)
1944              * Not Present in:
1945              *   pre-2000 models
1946              */
1947             // FALLTHROUGH
1948         case 0x2b:
1949             /* Request Fix and Channel Tracking info, Type 2
1950              * Present in:
1951              *   Copernicus II (2009)
1952              * Not Present in:
1953              *   pre-2000 models
1954              */
1955             // FALLTHROUGH
1956         case 0x41:
1957             /* Stored manufacturing operating parameters
1958              * Present in:
1959              *   ICM SMT 360 (2018)
1960              *   RES SMT 360 (2018)
1961              * Not Present in:
1962              *   pre-2000 models
1963              *   Copernicus II (2009)
1964              */
1965             // FALLTHROUGH
1966         case 0x42:
1967             /* Stored production parameters
1968              * Present in:
1969              *   ICM SMT 360 (2018)
1970              *   RES SMT 360 (2018)
1971              * Not Present in:
1972              *   pre-2000 models
1973              *   Copernicus II (2009)
1974              */
1975             // FALLTHROUGH
1976         case 0x4a:
1977             /* PPS characteristics
1978              * Present in:
1979              *   ICM SMT 360 (2018)
1980              *   RES SMT 360 (2018)
1981              *   Copernicus II (2009)
1982              * Not Present in:
1983              *   pre-2000 models
1984              */
1985             // FALLTHROUGH
1986         case 0x4e:
1987             /* PPS Output options
1988              * Present in:
1989              *   ICM SMT 360 (2018)
1990              *   RES SMT 360 (2018)
1991              * Not Present in:
1992              *   pre-2000 models
1993              *   Copernicus II (2009)
1994              */
1995             // FALLTHROUGH
1996         case 0x4f:
1997             /* Set PPS Width
1998              * Present in:
1999              *   Copernicus II (2009)
2000              * Not Present in:
2001              *   pre-2000 models
2002              *   ICM SMT 360 (2018)
2003              *   RES SMT 360 (2018)
2004              */
2005             // FALLTHROUGH
2006         case 0x60:
2007             /* DR Calibration and Status Report
2008              * Present in:
2009              *   pre-2000 models
2010              * Not Present in:
2011              *   Copernicus II (2009)
2012              *   ICM SMT 360 (2018)
2013              *   RES SMT 360 (2018)
2014              */
2015             // FALLTHROUGH
2016         case 0x62:
2017             /* GPS/DR Position/Velocity Report
2018              * Present in:
2019              *   pre-2000 models
2020              * Not Present in:
2021              *   Copernicus II (2009)
2022              *   ICM SMT 360 (2018)
2023              *   RES SMT 360 (2018)
2024              */
2025             // FALLTHROUGH
2026         case 0x64:
2027             /* Firmware Version and Configuration Report
2028              * Present in:
2029              *   pre-2000 models
2030              * Not Present in:
2031              *   Copernicus II (2009)
2032              *   ICM SMT 360 (2018)
2033              *   RES SMT 360 (2018)
2034              */
2035             // FALLTHROUGH
2036         case 0x6b:
2037             /* Last Gyroscope Readings Report
2038              * Present in:
2039              *   pre-2000 models
2040              * Not Present in:
2041              *   Copernicus II (2009)
2042              *   ICM SMT 360 (2018)
2043              *   RES SMT 360 (2018)
2044              */
2045             // FALLTHROUGH
2046         case 0x6d:
2047             /* Last Odometer Readings Report
2048              * Present in:
2049              *   pre-2000 models
2050              * Not Present in:
2051              *   Copernicus II (2009)
2052              *   ICM SMT 360 (2018)
2053              *   RES SMT 360 (2018)
2054              */
2055             // FALLTHROUGH
2056         case 0x6f:
2057             /* Firmware Version Name Report
2058              * Present in:
2059              *   pre-2000 models
2060              * Not Present in:
2061              *   Copernicus II (2009)
2062              *   ICM SMT 360 (2018)
2063              *   RES SMT 360 (2018)
2064              */
2065             // FALLTHROUGH
2066         case 0x70:
2067             /* Beacon Channel Status Report
2068              * Present in:
2069              *   pre-2000 models
2070              * Not Present in:
2071              *   Copernicus II (2009)
2072              *   ICM SMT 360 (2018)
2073              *   RES SMT 360 (2018)
2074              */
2075             // FALLTHROUGH
2076         case 0x71:
2077             /* DGPS Station Database Reports
2078              * Present in:
2079              *   pre-2000 models
2080              * Not Present in:
2081              *   Copernicus II (2009)
2082              *   ICM SMT 360 (2018)
2083              *   RES SMT 360 (2018)
2084              */
2085             // FALLTHROUGH
2086         case 0x73:
2087             /* Beacon Channel Control Acknowledgment
2088              * Present in:
2089              *   pre-2000 models
2090              * Not Present in:
2091              *   Copernicus II (2009)
2092              *   ICM SMT 360 (2018)
2093              *   RES SMT 360 (2018)
2094              */
2095             // FALLTHROUGH
2096         case 0x74:
2097             /* Clear Beacon Database Acknowledgment
2098              * Present in:
2099              *   pre-2000 models
2100              * Not Present in:
2101              *   Copernicus II (2009)
2102              *   ICM SMT 360 (2018)
2103              *   RES SMT 360 (2018)
2104              */
2105             // FALLTHROUGH
2106         case 0x75:
2107             /* FFT Start Acknowledgment
2108              * Present in:
2109              *   pre-2000 models
2110              * Not Present in:
2111              *   Copernicus II (2009)
2112              *   ICM SMT 360 (2018)
2113              *   RES SMT 360 (2018)
2114              */
2115             // FALLTHROUGH
2116         case 0x76:
2117             /* FFT Stop Acknowledgment
2118              * Present in:
2119              *   pre-2000 models
2120              * Not Present in:
2121              *   Copernicus II (2009)
2122              *   ICM SMT 360 (2018)
2123              *   RES SMT 360 (2018)
2124              */
2125             // FALLTHROUGH
2126         case 0x77:
2127             /* FFT Reports
2128              * Present in:
2129              *   pre-2000 models
2130              * Not Present in:
2131              *   Copernicus II (2009)
2132              *   ICM SMT 360 (2018)
2133              *   RES SMT 360 (2018)
2134              */
2135             // FALLTHROUGH
2136         case 0x78:
2137             /* RTCM Reports
2138              * Present in:
2139              *   pre-2000 models
2140              * Not Present in:
2141              *   Copernicus II (2009)
2142              *   ICM SMT 360 (2018)
2143              *   RES SMT 360 (2018)
2144              */
2145             // FALLTHROUGH
2146         case 0x79:
2147             /* Beacon Station Attributes Acknowledgment
2148              * Present in:
2149              *   pre-2000 models
2150              * Not Present in:
2151              *   Copernicus II (2009)
2152              *   ICM SMT 360 (2018)
2153              *   RES SMT 360 (2018)
2154              */
2155             // FALLTHROUGH
2156         case 0x7a:
2157             /* Beacon Station Attributes Report
2158              * Present in:
2159              *   pre-2000 models
2160              * Not Present in:
2161              *   Copernicus II (2009)
2162              *   ICM SMT 360 (2018)
2163              *   RES SMT 360 (2018)
2164              */
2165             // FALLTHROUGH
2166         case 0x7b:
2167             /* DGPS Receiver RAM Configuration Block Report
2168              * Present in:
2169              *   pre-2000 models
2170              * Not Present in:
2171              *   Copernicus II (2009)
2172              *   ICM SMT 360 (2018)
2173              *   RES SMT 360 (2018)
2174              */
2175             // FALLTHROUGH
2176         case 0x7c:
2177             /* DGPS Receiver Configuration Block Acknowledgment
2178              * Present in:
2179              *   pre-2000 models
2180              * Not Present in:
2181              *   Copernicus II (2009)
2182              *   ICM SMT 360 (2018)
2183              *   RES SMT 360 (2018)
2184              */
2185             // FALLTHROUGH
2186         case 0x7e:
2187             /* Satellite Line-of-Sight (LOS) Message
2188              * Present in:
2189              *   pre-2000 models
2190              * Not Present in:
2191              *   Copernicus II (2009)
2192              *   ICM SMT 360 (2018)
2193              *   RES SMT 360 (2018)
2194              */
2195             // FALLTHROUGH
2196         case 0x7f:
2197             /* DGPS Receiver ROM Configuration Block Report
2198              * Present in:
2199              *   pre-2000 models
2200              * Not Present in:
2201              *   Copernicus II (2009)
2202              *   ICM SMT 360 (2018)
2203              *   RES SMT 360 (2018)
2204              */
2205             // FALLTHROUGH
2206         case 0x80:
2207             /* DGPS Service Provider System Information Report
2208              * Present in:
2209              *   pre-2000 models
2210              * Not Present in:
2211              *   Copernicus II (2009)
2212              *   ICM SMT 360 (2018)
2213              *   RES SMT 360 (2018)
2214              */
2215             // FALLTHROUGH
2216         case 0x81:
2217             /* Decoder Station Information Report and Selection Acknowledgment
2218              * Present in:
2219              *   pre-2000 models
2220              * Not Present in:
2221              *   Copernicus II (2009)
2222              *   ICM SMT 360 (2018)
2223              *   RES SMT 360 (2018)
2224              */
2225             // FALLTHROUGH
2226         case 0x82:
2227             /* Decoder Diagnostic Information Report
2228              * Present in:
2229              *   pre-2000 models
2230              * Not Present in:
2231              *   Copernicus II (2009)
2232              *   ICM SMT 360 (2018)
2233              *   RES SMT 360 (2018)
2234              */
2235             // FALLTHROUGH
2236         case 0x84:
2237             /* Satellite FFT Control Acknowledgment
2238              * Present in:
2239              *   pre-2000 models
2240              * Not Present in:
2241              *   Copernicus II (2009)
2242              *   ICM SMT 360 (2018)
2243              *   RES SMT 360 (2018)
2244              */
2245             // FALLTHROUGH
2246         case 0x85:
2247             /* DGPS Source Tracking Status Report
2248              * Present in:
2249              *   pre-2000 models
2250              * Not Present in:
2251              *   Copernicus II (2009)
2252              *   ICM SMT 360 (2018)
2253              *   RES SMT 360 (2018)
2254              */
2255             // FALLTHROUGH
2256         case 0x86:
2257             /* Clear Satellite Database Acknowledgment
2258              * Present in:
2259              *   pre-2000 models
2260              * Not Present in:
2261              *   Copernicus II (2009)
2262              *   ICM SMT 360 (2018)
2263              *   RES SMT 360 (2018)
2264              */
2265             // FALLTHROUGH
2266         case 0x87:
2267             /* Network Statistics Report
2268              * Present in:
2269              *   pre-2000 models
2270              * Not Present in:
2271              *   Copernicus II (2009)
2272              *   ICM SMT 360 (2018)
2273              *   RES SMT 360 (2018)
2274              */
2275             // FALLTHROUGH
2276         case 0x88:
2277             /* Diagnostic Output Options Report
2278              * Present in:
2279              *   pre-2000 models
2280              * Not Present in:
2281              *   Copernicus II (2009)
2282              *   ICM SMT 360 (2018)
2283              *   RES SMT 360 (2018)
2284              */
2285             // FALLTHROUGH
2286         case 0x89:
2287             /* DGPS Source Control Report /Acknowledgment
2288              * Present in:
2289              *   pre-2000 models
2290              * Not Present in:
2291              *   Copernicus II (2009)
2292              *   ICM SMT 360 (2018)
2293              *   RES SMT 360 (2018)
2294              */
2295             // FALLTHROUGH
2296         case 0x8a:
2297             /* Service Provider Information Report and Acknowledgment
2298              * Present in:
2299              *   pre-2000 models
2300              * Not Present in:
2301              *   Copernicus II (2009)
2302              *   ICM SMT 360 (2018)
2303              *   RES SMT 360 (2018)
2304              */
2305             // FALLTHROUGH
2306         case 0x8b:
2307             /* Service Provider Activation Information Report & Acknowledgment
2308              * Present in:
2309              *   pre-2000 models
2310              * Not Present in:
2311              *   Copernicus II (2009)
2312              *   ICM SMT 360 (2018)
2313              *   RES SMT 360 (2018)
2314              */
2315             // FALLTHROUGH
2316         case 0x8e:
2317             /* Service Provider Data Load Report
2318              * Present in:
2319              *   pre-2000 models
2320              * Not Present in:
2321              *   Copernicus II (2009)
2322              *   ICM SMT 360 (2018)
2323              *   RES SMT 360 (2018)
2324              */
2325             // FALLTHROUGH
2326         case 0x8f:
2327             /* Receiver Identity Report
2328              * Present in:
2329              *   pre-2000 models
2330              * Not Present in:
2331              *   Copernicus II (2009)
2332              *   ICM SMT 360 (2018)
2333              *   RES SMT 360 (2018)
2334              */
2335             // FALLTHROUGH
2336         case 0x90:
2337             /* Guidance Status Report
2338              * Present in:
2339              *   pre-2000 models
2340              * Not Present in:
2341              *   Copernicus II (2009)
2342              *   ICM SMT 360 (2018)
2343              *   RES SMT 360 (2018)
2344              */
2345             // FALLTHROUGH
2346         case 0x91:
2347             /* Guidance Configuration Report
2348              * Present in:
2349              *   pre-2000 models
2350              * Not Present in:
2351              *   Copernicus II (2009)
2352              *   ICM SMT 360 (2018)
2353              *   RES SMT 360 (2018)
2354              */
2355             // FALLTHROUGH
2356         case 0x92:
2357             /* Lightbar Configuration Report
2358              * Present in:
2359              *   pre-2000 models
2360              * Not Present in:
2361              *   Copernicus II (2009)
2362              *   ICM SMT 360 (2018)
2363              *   RES SMT 360 (2018)
2364              */
2365             // FALLTHROUGH
2366         case 0x94:
2367             /* Guidance Operation Acknowledgment
2368              * Present in:
2369              *   pre-2000 models
2370              * Not Present in:
2371              *   Copernicus II (2009)
2372              *   ICM SMT 360 (2018)
2373              *   RES SMT 360 (2018)
2374              */
2375             // FALLTHROUGH
2376         case 0x95:
2377             /* Button Box Configuration Type Report
2378              * Present in:
2379              *   pre-2000 models
2380              * Not Present in:
2381              *   Copernicus II (2009)
2382              *   ICM SMT 360 (2018)
2383              *   RES SMT 360 (2018)
2384              */
2385             // FALLTHROUGH
2386         case 0x96:
2387             /* Point Manipulation Report
2388              * Present in:
2389              *   pre-2000 models
2390              * Not Present in:
2391              *   Copernicus II (2009)
2392              *   ICM SMT 360 (2018)
2393              *   RES SMT 360 (2018)
2394              */
2395             // FALLTHROUGH
2396         case 0x97:
2397             /* Utility Information Report
2398              * Present in:
2399              *   pre-2000 models
2400              * Not Present in:
2401              *   Copernicus II (2009)
2402              *   ICM SMT 360 (2018)
2403              *   RES SMT 360 (2018)
2404              */
2405             // FALLTHROUGH
2406         case 0x98:
2407             /* Individual Button Configuration Report
2408              * Present in:
2409              *   pre-2000 models
2410              * Not Present in:
2411              *   Copernicus II (2009)
2412              *   ICM SMT 360 (2018)
2413              *   RES SMT 360 (2018)
2414              */
2415             // FALLTHROUGH
2416         case 0x9a:
2417             /* Differential Correction Information Report
2418              * Present in:
2419              *   pre-2000 models
2420              * Not Present in:
2421              *   Copernicus II (2009)
2422              *   ICM SMT 360 (2018)
2423              *   RES SMT 360 (2018)
2424              */
2425             // FALLTHROUGH
2426         case 0xa0:
2427             /* DAC value
2428              * Present in:
2429              *   ICM SMT 360 (2018)
2430              *   RES SMT 360 (2018)
2431              * Not Present in:
2432              *   pre-2000 models
2433              *   Copernicus II (2009)
2434              */
2435             // FALLTHROUGH
2436         case 0xa2:
2437             /* UTC/GPS timing
2438              * Present in:
2439              *   ICM SMT 360 (2018)
2440              *   RES SMT 360 (2018)
2441              * Not Present in:
2442              *   pre-2000 models
2443              *   Copernicus II (2009)
2444              */
2445             // FALLTHROUGH
2446         case 0xa3:
2447             /* Oscillator disciplining command
2448              * Present in:
2449              *   ICM SMT 360 (2018)
2450              *   RES SMT 360 (2018)
2451              * Not Present in:
2452              *   pre-2000 models
2453              *   Copernicus II (2009)
2454              */
2455             // FALLTHROUGH
2456         case 0xa8:
2457             /* Oscillator disciplining parameters
2458              * Present in:
2459              *   ICM SMT 360 (2018)
2460              *   RES SMT 360 (2018)
2461              * Not Present in:
2462              *   pre-2000 models
2463              *   Copernicus II (2009)
2464              */
2465             // FALLTHROUGH
2466         case 0xa9:
2467             /* self-survey parameters
2468              * Present in:
2469              *   ICM SMT 360 (2018)
2470              *   RES SMT 360 (2018)
2471              * Not Present in:
2472              *   pre-2000 models
2473              *   Copernicus II (2009)
2474              */
2475             // FALLTHROUGH
2476         default:
2477             GPSD_LOG(LOG_WARN, &session->context->errout,
2478                      "TSIP: Unhandled TSIP superpacket type 0x8f-%02x\n",
2479                      u1);
2480         }
2481         break;
2482     case 0xbb:
2483         /* Navigation Configuration
2484          * Present in:
2485          *   pre-2000 models
2486          *   Copernicus II (2009)
2487          *   ICM SMT 360 (2018)
2488          *   RES SMT 360 (2018)
2489          */
2490         if (len != 40 && len != 43) {
2491             /* see packet.c for explamation */
2492             bad_len = 40;
2493             break;
2494         }
2495         u1 = getub(buf, 0);             // Subcode, always zero?
2496         u2 = getub(buf, 1);             // Operating Dimension
2497         u3 = getub(buf, 2);             // DGPS Mode (not in Acutime Gold)
2498         u4 = getub(buf, 3);             // Dynamics Code
2499         f1 = getbef32((char *)buf, 5);  // Elevation Mask
2500         f2 = getbef32((char *)buf, 9);  // AMU Mask
2501         f3 = getbef32((char *)buf, 13); // DOP Mask
2502         f4 = getbef32((char *)buf, 17); // DOP Switch
2503         u5 = getub(buf, 21);            // DGPS Age Limit (not in Acutime Gold)
2504         /* Constellation
2505          * bit 0 - GPS
2506          * bit 1 - GLONASS
2507          * bit 2 - reserved
2508          * bit 3 - BeiDou
2509          * bit 4 - Galileo
2510          * bit 5 - QZSS
2511          * bit 6 - reserved
2512          * bit 7 - reserved
2513          */
2514         u6 = getub(buf, 27);
2515         GPSD_LOG(LOG_PROG, &session->context->errout,
2516                  "TSIP: Navigation Configuration (0xbb) %u %u %u %u %f %f %f "
2517                  "%f %u x%x\n",
2518                  u1, u2, u3, u4, f1, f2, f3, f4, u5, u6);
2519         // RES SMT 360 defaults to Mode 7, Constellation 3
2520         break;
2521 
2522     case 0x1a:
2523         /* TSIP RTCM Wrapper Command
2524          * Present in:
2525          *   pre-2000 models
2526          * Not Present in:
2527          *   ICM SMT 360 (2018)
2528          *   RES SMT 360 (2018)
2529          *   Copernicus II (2009)
2530          */
2531         // FALLTHROUGH
2532     case 0x2e:
2533         /* Request GPS Time
2534          * Present in:
2535          *   ICM SMT 360 (2018)
2536          *   RES SMT 360 (2018)
2537          * Not Present in:
2538          *   pre-2000 models
2539          *   Copernicus II (2009)
2540          */
2541         // FALLTHROUGH
2542     case 0x32:
2543         /* Request Unit Position
2544          * Present in:
2545          *   ICM SMT 360 (2018)
2546          *   RES SMT 360 (2018)
2547          * Not Present in:
2548          *   pre-2000 models
2549          *   Copernicus II (2009)
2550          */
2551         // FALLTHROUGH
2552     case 0x38:
2553         /* Request SV System data
2554          * Present in:
2555          *   ICM SMT 360 (2018)
2556          *   RES SMT 360 (2018)
2557          * Not Present in:
2558          *   pre-2000 models
2559          *   Copernicus II (2009)
2560          */
2561         // FALLTHROUGH
2562     case 0x40:
2563         /* Almanac Data for Single Satellite Report
2564          * Present in:
2565          *   pre-2000 models
2566          * Not Present in:
2567          *   ICM SMT 360 (2018)
2568          *   RES SMT 360 (2018)
2569          *   Copernicus II (2009)
2570          */
2571         // FALLTHROUGH
2572     case 0x44:
2573         /* Non-Overdetermined Satellite Selection Report
2574          * Present in:
2575          *   pre-2000 models
2576          * Not Present in:
2577          *   ICM SMT 360 (2018)
2578          *   RES SMT 360 (2018)
2579          *   Copernicus II (2009)
2580          */
2581         // FALLTHROUGH
2582     case 0x49:
2583         /* Almanac Health Page
2584          * Present in:
2585          *   pre-2000 models
2586          * Not Present in:
2587          *   Copernicus II (2009)
2588          */
2589         // FALLTHROUGH
2590     case 0x4d:
2591         /* Oscillator Offset
2592          * Present in:
2593          *   pre-2000 models
2594          *   Copernicus II (2009)
2595          */
2596         // FALLTHROUGH
2597     case 0x4e:
2598         /* Response to set GPS time
2599          * Present in:
2600          *   pre-2000 models
2601          *   Copernicus II (2009)
2602          *   ICM SMT 360 (2018)
2603          *   RES SMT 360 (2018)
2604          */
2605         // FALLTHROUGH
2606     case 0x4f:
2607         /* UTC Parameters Report
2608          * Present in:
2609          *   pre-2000 models
2610          * Not Present in:
2611          *   ICM SMT 360 (2018)
2612          *   RES SMT 360 (2018)
2613          *   Copernicus II (2009)
2614          */
2615         // FALLTHROUGH
2616     case 0x53:
2617         /* Analog-to-Digital Readings Report
2618          * Present in:
2619          *   pre-2000 models
2620          * Not Present in:
2621          *   ICM SMT 360 (2018)
2622          *   RES SMT 360 (2018)
2623          *   Copernicus II (2009)
2624          */
2625         // FALLTHROUGH
2626     case 0x58:
2627         /* Satellite System Data/Acknowledge from Receiver
2628          * Present in:
2629          *   pre-2000 models
2630          *   Copernicus II (2009)
2631          *   ICM SMT 360 (2018)
2632          *   RES SMT 360 (2018)
2633          */
2634         // FALLTHROUGH
2635     case 0x59:
2636         /* Status of Satellite Disable or Ignore Health
2637          * aka Satellite Attribute Database Status Report
2638          * Present in:
2639          *   pre-2000 models
2640          *   ICM SMT 360 (2018)
2641          *   RES SMT 360 (2018)
2642          * Not Present in:
2643          *   Copernicus II (2009)
2644          */
2645         // FALLTHROUGH
2646     case 0x5b:
2647         /* Satellite Ephemeris Status
2648          * Present in:
2649          *   pre-2000 models
2650          * Not Present in:
2651          *   Copernicus II (2009)
2652          *   ICM SMT 360 (2018)
2653          *   RES SMT 360 (2018)
2654          */
2655         // FALLTHROUGH
2656     case 0x5e:
2657         /* Additional Fix Status Report
2658          * Present in:
2659          *   pre-2000 models
2660          * Not Present in:
2661          *   Copernicus II (2009)
2662          *   ICM SMT 360 (2018)
2663          *   RES SMT 360 (2018)
2664          */
2665         // FALLTHROUGH
2666     case 0x5f:
2667         /* Severe Failure Notification
2668          * Present in:
2669          *   pre-2000 models
2670          * Not Present in:
2671          *   ICM SMT 360 (2018)
2672          *   RES SMT 360 (2018)
2673          *   Copernicus II (2009)
2674          */
2675         // FALLTHROUGH
2676     case 0x60:
2677         /* Differential GPS Pseudorange Corrections Report
2678          * Present in:
2679          *   pre-2000 models
2680          * Not Present in:
2681          *   ICM SMT 360 (2018)
2682          *   RES SMT 360 (2018)
2683          *   Copernicus II (2009)
2684          */
2685         // FALLTHROUGH
2686     case 0x61:
2687         /* Differential GPS Delta Pseudorange Corrections Report
2688          * Present in:
2689          *   pre-2000 models
2690          * Not Present in:
2691          *   ICM SMT 360 (2018)
2692          *   RES SMT 360 (2018)
2693          *   Copernicus II (2009)
2694          */
2695         // FALLTHROUGH
2696     case 0x6a:
2697         /* Differential Corrections Used in the Fix Repor
2698          * Present in:
2699          *   pre-2000 models
2700          * Not Present in:
2701          *   ICM SMT 360 (2018)
2702          *   RES SMT 360 (2018)
2703          *   Copernicus II (2009)
2704          */
2705         // FALLTHROUGH
2706     case 0x6e:
2707         /* Synchronized Measurements
2708          * Present in:
2709          *   pre-2000 models
2710          * Not Present in:
2711          *   Copernicus II (2009)
2712          *   ICM SMT 360 (2018)
2713          *   RES SMT 360 (2018)
2714          */
2715         // FALLTHROUGH
2716     case 0x6f:
2717         /* Synchronized Measurements Report
2718          * Present in:
2719          *   pre-2000 models
2720          * Not Present in:
2721          *   Copernicus II (2009)
2722          *   ICM SMT 360 (2018)
2723          *   RES SMT 360 (2018)
2724          */
2725         // FALLTHROUGH
2726     case 0x70:
2727         /* Filter Report
2728          * Present in:
2729          *   pre-2000 models
2730          * Not Present in:
2731          *   Copernicus II (2009)
2732          *   ICM SMT 360 (2018)
2733          *   RES SMT 360 (2018)
2734          */
2735         // FALLTHROUGH
2736     case 0x76:
2737         /* Overdetermined Mode Report
2738          * Present in:
2739          *   pre-2000 models
2740          * Not Present in:
2741          *   ICM SMT 360 (2018)
2742          *   RES SMT 360 (2018)
2743          *   Copernicus II (2009)
2744          */
2745         // FALLTHROUGH
2746     case 0x78:
2747         /* Maximum PRC Age Report
2748          * Present in:
2749          *   pre-2000 models
2750          * Not Present in:
2751          *   ICM SMT 360 (2018)
2752          *   RES SMT 360 (2018)
2753          *   Copernicus II (2009)
2754          */
2755         // FALLTHROUGH
2756     case 0x7a:
2757         /* NMEA settings
2758          * Not Present in:
2759          *   pre-2000 models
2760          *   Copernicus II (2009)
2761          *   ICM SMT 360 (2018)
2762          *   RES SMT 360 (2018)
2763          */
2764         // FALLTHROUGH
2765     case 0x7b:
2766         /* NMEA interval and message mask response
2767          * Present in:
2768          *   pre-2000 models
2769          *   ICM SMT 360 (2018)
2770          *   RES SMT 360 (2018)
2771          * Not Present in:
2772          *   Copernicus II (2009)
2773          */
2774         // FALLTHROUGH
2775     case 0x7d:
2776         /* Position Fix Rate Configuration Reports
2777          * Present in:
2778          *   pre-2000 models
2779          * Not Present in:
2780          *   ICM SMT 360 (2018)
2781          *   RES SMT 360 (2018)
2782          *   Copernicus II (2009)
2783          */
2784         // FALLTHROUGH
2785     case 0x85:
2786         /* Differential Correction Status Report
2787          * Present in:
2788          *   pre-2000 models
2789          * Not Present in:
2790          *   ICM SMT 360 (2018)
2791          *   RES SMT 360 (2018)
2792          *   Copernicus II (2009)
2793          */
2794         // FALLTHROUGH
2795     case 0x87:
2796         /* Reference Station Parameters Report
2797          * Present in:
2798          *   pre-2000 models
2799          * Not Present in:
2800          *   ICM SMT 360 (2018)
2801          *   RES SMT 360 (2018)
2802          *   Copernicus II (2009)
2803          */
2804         // FALLTHROUGH
2805     case 0x89:
2806         /* Receiver acquisition sensitivity mode
2807          * Present in:
2808          *   Copernicus II (2009)
2809          * Not Present in:
2810          *   pre-2000 models
2811          *   ICM SMT 360 (2018)
2812          *   RES SMT 360 (2018)
2813          */
2814         // FALLTHROUGH
2815     case 0x88:
2816         /* Mobile Differential Parameters Report
2817          * Present in:
2818          *   pre-2000 models
2819          * Not Present in:
2820          *   ICM SMT 360 (2018)
2821          *   RES SMT 360 (2018)
2822          *   Copernicus II (2009)
2823          */
2824         // FALLTHROUGH
2825     case 0x8b:
2826         /* QA/QC Reports
2827          * Present in:
2828          *   pre-2000 models
2829          * Not Present in:
2830          *   ICM SMT 360 (2018)
2831          *   RES SMT 360 (2018)
2832          *   Copernicus II (2009)
2833          */
2834         // FALLTHROUGH
2835     case 0x8d:
2836         /* Average Position Reports
2837          * Present in:
2838          *   pre-2000 models
2839          * Not Present in:
2840          *   ICM SMT 360 (2018)
2841          *   RES SMT 360 (2018)
2842          *   Copernicus II (2009)
2843          */
2844         // FALLTHROUGH
2845     case 0xb0:
2846         /* PPS and Event Report Packets
2847          * Present in:
2848          *   pre-2000 models
2849          * Not Present in:
2850          *   ICM SMT 360 (2018)
2851          *   RES SMT 360 (2018)
2852          *   Copernicus II (2009)
2853          */
2854         // FALLTHROUGH
2855     case 0xbc:
2856         /* Receiver port configuration
2857          * Present in:
2858          *   pre-2000 models
2859          *   Copernicus II (2009)
2860          * Not Present in:
2861          *   ICM SMT 360 (2018)
2862          *   RES SMT 360 (2018)
2863          */
2864         // FALLTHROUGH
2865     case 0xc1:
2866         /* Bit Mask for GPIOs in Standby Mode
2867          * Present in:
2868          *   Copernicus II (2009)
2869          *   ICM SMT 360 (2018)
2870          *   RES SMT 360 (2018)
2871          * Not Present in:
2872          *   pre-2000 models
2873          */
2874         // FALLTHROUGH
2875     case 0xc2:
2876         /* SBAS SV Mask
2877          * Present in:
2878          *   Copernicus II (2009)
2879          *   ICM SMT 360 (2018)
2880          *   RES SMT 360 (2018)
2881          * Not Present in:
2882          *   pre-2000 models
2883          */
2884         // FALLTHROUGH
2885     default:
2886         GPSD_LOG(LOG_WARN, &session->context->errout,
2887                  "TSIP: Unhandled packet type x%02x\n", id);
2888         break;
2889     }
2890 
2891 #ifdef __UNUSED__
2892 // #if 1
2893     // full reset
2894     putbyte(buf, 0, 0x46);
2895     (void)tsip_write(session, 0x1e, buf, 1);
2896 #endif
2897 
2898     if (bad_len) {
2899         GPSD_LOG(LOG_WARNING, &session->context->errout,
2900                  "TSIP: ID x%02x wrong len %d s/b >= %d \n", id, len, bad_len);
2901     } else {
2902         GPSD_LOG(LOG_PROG, &session->context->errout,
2903                  "TSIP: ID x%02x mask %s\n", id, gps_maskdump(mask));
2904     }
2905     /* See if it is time to send some request packets for reports that.
2906      * The receiver won't send at fixed intervals.
2907      * Use llabs() as time sometimes goes backwards. */
2908 
2909     if (5 < llabs(now - session->driver.tsip.last_41)) {
2910         /* Request Current Time returns 0x41.
2911          * Easiest way to get GPS weeks and current leap seconds */
2912         (void)tsip_write(session, 0x21, buf, 0);
2913         session->driver.tsip.last_41 = now;
2914     }
2915 
2916     if (5 < llabs(now - session->driver.tsip.last_6d)) {
2917         /* Request GPS Receiver Position Fix Mode
2918          * Returns 0x44, 0x6c, or 0x6d. */
2919         (void)tsip_write(session, 0x24, buf, 0);
2920         session->driver.tsip.last_6d = now;
2921 #ifdef __UNUSED__
2922 // #if 1
2923         // request Receiver Configuration (0xbb)
2924         putbyte(buf, 0, 0x00);
2925         (void)tsip_write(session, 0xbb, buf, 1);
2926         // request Packet Broadcast Mask
2927         putbyte(buf, 0, 0xa5);
2928         (void)tsip_write(session, 0x8e, buf, 1);
2929 #endif // UNUSED
2930     }
2931 
2932     if (1 > session->driver.tsip.superpkt &&
2933         60 < llabs(now - session->driver.tsip.last_48)) {
2934         /* Request GPS System Message
2935          * Returns 0x48.
2936          * not supported on:
2937          *  Lassen SQ (2002)
2938          *  Lassen iQ (2005)
2939          *  ICM SMT 360
2940          *  RES SMT 360
2941          *  and post 2005
2942          * SuperPackets replaced 0x28 */
2943         (void)tsip_write(session, 0x28, buf, 0);
2944         session->driver.tsip.last_48 = now;
2945     }
2946 
2947     if (5 < llabs(now - session->driver.tsip.last_5c)) {
2948         /* Request Current Satellite Tracking Status
2949          * Returns: 0x5c or 0x5d
2950          *  5c from GPS only devices
2951          *  5d from multi-gnss devices */
2952         putbyte(buf, 0, 0x00);  /* All satellites */
2953         (void)tsip_write(session, 0x3c, buf, 1);
2954         session->driver.tsip.last_5c = now;
2955     }
2956 
2957     if (5 < llabs(now - session->driver.tsip.last_46)) {
2958         /* Request Health of Receiver
2959          * Returns 0x46 and 0x4b. */
2960         (void)tsip_write(session, 0x26, buf, 0);
2961         session->driver.tsip.last_46 = now;
2962     }
2963     if ((session->driver.tsip.req_compact > 0) &&
2964         (5 < llabs(now - session->driver.tsip.req_compact))) {
2965         /* Compact Superpacket requested but no response
2966          * Not in:
2967          * ICM SMT 360
2968          * RES SMT 360
2969           */
2970         session->driver.tsip.req_compact = 0;
2971         GPSD_LOG(LOG_WARN, &session->context->errout,
2972                  "TSIP: No Compact Super Packet (0x8f-23), "
2973                  "try LFwEI (0x8f-20)\n");
2974 
2975         /* Request LFwEI Super Packet 0x8f-20 */
2976         putbyte(buf, 0, 0x20);
2977         putbyte(buf, 1, 0x01);  /* enabled */
2978         (void)tsip_write(session, 0x8e, buf, 2);
2979     }
2980 
2981     return mask;
2982 }
2983 
2984 #ifdef CONTROLSEND_ENABLE
tsip_control_send(struct gps_device_t * session,char * buf,size_t buflen)2985 static ssize_t tsip_control_send(struct gps_device_t *session,
2986                                  char *buf, size_t buflen)
2987 /* not used by the daemon, it's for gpsctl and friends */
2988 {
2989     return (ssize_t) tsip_write(session,
2990                                 (unsigned int)buf[0],
2991                                 (unsigned char *)buf + 1, buflen - 1);
2992 }
2993 #endif /* CONTROLSEND_ENABLE */
2994 
tsip_init_query(struct gps_device_t * session)2995 static void tsip_init_query(struct gps_device_t *session)
2996 {
2997     unsigned char buf[100];
2998 
2999     /* Use 0x1C-03 to Request Hardware Version Information (0x1C-83) */
3000     putbyte(buf, 0, 0x03); /* Subcode */
3001     (void)tsip_write(session, 0x1c, buf, 1);
3002     /*
3003      * After HW information packet is received, a
3004      * decision is made how to configure the device.
3005      */
3006 }
3007 
tsip_event_hook(struct gps_device_t * session,event_t event)3008 static void tsip_event_hook(struct gps_device_t *session, event_t event)
3009 {
3010     unsigned char buf[100];
3011 
3012     GPSD_LOG(LOG_SPIN, &session->context->errout,
3013              "TSIP: event_hook event %d ro %d\n",
3014              event, session->context->readonly);
3015 
3016     if (session->context->readonly)
3017         return;
3018     switch (event) {
3019     case event_identified:
3020         // FALLTHROUGH
3021     case event_reactivate:
3022         // FIXME: reactivate style should depend on model
3023         /*
3024          * Set basic configuration, using Set or Request I/O Options (0x35).
3025          * in case no hardware config response comes back.
3026          */
3027         /* Position: enable: Double Precision, LLA
3028          *           disable: ECEF */
3029         putbyte(buf, 0, IO1_8F20|IO1_DP|IO1_LLA);
3030         /* Velocity: enable: ENU, disable vECEF */
3031         putbyte(buf, 1, IO2_ENU);
3032         /* Time: enable: 0x42, 0x43, 0x4a
3033          *       disable: 0x83, 0x84, 0x56 */
3034         putbyte(buf, 2, 0x00);
3035         /* Aux: enable: 0x5A, dBHz */
3036         putbyte(buf, 3, IO4_DBHZ);
3037         (void)tsip_write(session, 0x35, buf, 4);
3038         break;
3039     case event_configure:
3040         // this seems to get called on every packet...
3041         if (session->lexer.counter == 0) {
3042             /* but the above if() makes it never execute
3043              * formerely tried to force 801 here, but luckily it
3044              * never fired as some Trimble are 8N1 */
3045         }
3046         break;
3047     case event_deactivate:
3048         // used to revert serial port parms here.  No need for that.
3049         // FALLTHROUGH
3050     default:
3051         break;
3052     }
3053 }
3054 
3055 #ifdef RECONFIGURE_ENABLE
tsip_speed_switch(struct gps_device_t * session,speed_t speed,char parity,int stopbits)3056 static bool tsip_speed_switch(struct gps_device_t *session,
3057                               speed_t speed, char parity, int stopbits)
3058 {
3059     unsigned char buf[100];
3060 
3061     switch (parity) {
3062     case 'E':
3063     case 2:
3064         parity = (char)2;
3065         break;
3066     case 'O':
3067     case 1:
3068         parity = (char)1;
3069         break;
3070     case 'N':
3071     case 0:
3072     default:
3073         parity = (char)0;
3074         break;
3075     }
3076 
3077     /* Set Port Configuration (0xbc) */
3078     putbyte(buf, 0, 0xff);      /* current port */
3079     /* input dev.baudrate */
3080     putbyte(buf, 1, (round(log((double)speed / 300) / GPS_LN2)) + 2);
3081     putbyte(buf, 2, getub(buf, 1));     /* output baudrate */
3082     putbyte(buf, 3, 3);         /* character width (8 bits) */
3083     putbyte(buf, 4, parity);    /* parity (normally odd) */
3084     putbyte(buf, 5, stopbits - 1);      /* stop bits (normally 1 stopbit) */
3085     putbyte(buf, 6, 0);         /* flow control (none) */
3086     putbyte(buf, 7, 0x02);      /* input protocol (TSIP) */
3087     putbyte(buf, 8, 0x02);      /* output protocol (TSIP) */
3088     putbyte(buf, 9, 0);         /* reserved */
3089     (void)tsip_write(session, 0xbc, buf, 10);
3090 
3091     return true;                /* it would be nice to error-check this */
3092 }
3093 
tsip_mode(struct gps_device_t * session,int mode)3094 static void tsip_mode(struct gps_device_t *session, int mode)
3095 {
3096     if (mode == MODE_NMEA) {
3097         unsigned char buf[16];
3098 
3099         /* send NMEA Interval and Message Mask Command (0x7a)
3100         * First turn on the NMEA messages we want */
3101         putbyte(buf, 0, 0x00);  /* subcode 0 */
3102         putbyte(buf, 1, 0x01);  /* 1-second fix interval */
3103         putbyte(buf, 2, 0x00);  /* Reserved */
3104         putbyte(buf, 3, 0x00);  /* Reserved */
3105         putbyte(buf, 4, 0x01);  /* 1=GST, Reserved */
3106         /* 1=GGA, 2=GGL, 4=VTG, 8=GSV, */
3107         /* 0x10=GSA, 0x20=ZDA, 0x40=Reserved, 0x80=RMC  */
3108         putbyte(buf, 5, 0x19);
3109 
3110         (void)tsip_write(session, 0x7A, buf, 6);
3111 
3112         /* Now switch to NMEA mode */
3113 
3114         memset(buf, 0, sizeof(buf));
3115 
3116         /* Set Port Configuration (0xbc) */
3117         // 4800, really?
3118         putbyte(buf, 0, 0xff);  /* current port */
3119         putbyte(buf, 1, 0x06);  /* 4800 bps input */
3120         putbyte(buf, 2, 0x06);  /* 4800 bps output */
3121         putbyte(buf, 3, 0x03);  /* 8 data bits */
3122         putbyte(buf, 4, 0x00);  /* No parity */
3123         putbyte(buf, 5, 0x00);  /* 1 stop bit */
3124         putbyte(buf, 6, 0x00);  /* No flow control */
3125         putbyte(buf, 7, 0x02);  /* Input protocol TSIP */
3126         putbyte(buf, 8, 0x04);  /* Output protocol NMEA */
3127         putbyte(buf, 9, 0x00);  /* Reserved */
3128 
3129         (void)tsip_write(session, 0xBC, buf, 10);
3130 
3131     } else if (mode == MODE_BINARY) {
3132         /* The speed switcher also puts us back in TSIP, so call it */
3133         /* with the default 9600 8O1. */
3134         // FIXME: Should preserve the current speed.
3135         // (void)tsip_speed_switch(session, 9600, 'O', 1);
3136         // FIXME: should config TSIP binary!
3137         ;
3138 
3139     } else {
3140         GPSD_LOG(LOG_ERROR, &session->context->errout,
3141                  "TSIP: unknown mode %i requested\n", mode);
3142     }
3143 }
3144 #endif /* RECONFIGURE_ENABLE */
3145 
3146 /* configure generic Trimble TSIP device to a known state */
configuration_packets_generic(struct gps_device_t * session)3147 void configuration_packets_generic(struct gps_device_t *session)
3148 {
3149         unsigned char buf[100];
3150 
3151         GPSD_LOG(LOG_PROG, &session->context->errout,
3152                  "TSIP: configuration_packets_generic()\n");
3153 
3154         // Set basic configuration, using Set or Request I/O Options (0x35).
3155         /* Position: enable: Double Precision, LLA
3156          *           disable: ECEF */
3157         putbyte(buf, 0, IO1_8F20|IO1_DP|IO1_LLA);
3158         /* Velocity: enable: ENU, disable ECEF */
3159         putbyte(buf, 1, IO2_ENU);
3160         /* Time: enable: 0x42, 0x43, 0x4a
3161          *       disable: 0x83, 0x84, 0x56 */
3162         putbyte(buf, 2, 0x00);
3163         /* Aux: enable: 0x5A, dBHz */
3164         putbyte(buf, 3, IO4_DBHZ);
3165         (void)tsip_write(session, 0x35, buf, 4);
3166 
3167         /* Request Software Version (0x1f), returns 0x45 */
3168         (void)tsip_write(session, 0x1f, NULL, 0);
3169 
3170         /* Current Time Request (0x21), returns 0x41 */
3171         (void)tsip_write(session, 0x21, NULL, 0);
3172 
3173         /* Set Operating Parameters (0x2c)
3174          * not present in:
3175          *   Lassen SQ (2002)
3176          *   Lassen iQ (2005)
3177          *   RES SMT 360 */
3178         /* dynamics code: enabled: 1=land
3179          *   disabled: 2=sea, 3=air, 4=static
3180          *   default is land */
3181         putbyte(buf, 0, 0x01);
3182         /* elevation mask, 10 degrees is a common default,
3183          * TSIP default is 15 */
3184         putbef32((char *)buf, 1, (float)10.0 * DEG_2_RAD);
3185         /* signal level mask
3186          * default is 2.0 AMU. 5.0 to 6.0 for high accuracy */
3187         putbef32((char *)buf, 5, (float)06.0);
3188         /* PDOP mask
3189          * default is 12. 5.0 to 6.0 for high accuracy */
3190         putbef32((char *)buf, 9, (float)8.0);
3191         /* PDOP switch
3192          * default is 8.0 */
3193         putbef32((char *)buf, 13, (float)6.0);
3194         (void)tsip_write(session, 0x2c, buf, 17);
3195 
3196         /* Set Position Fix Mode (0x22)
3197          * 0=auto 2D/3D, 1=time only, 3=2D, 4=3D, 10=Overdetermined clock */
3198         putbyte(buf, 0, 0x00);
3199         (void)tsip_write(session, 0x22, buf, 1);
3200 
3201         /* Request GPS System Message (0x48)
3202          * not supported on model RES SMT 360 */
3203         (void)tsip_write(session, 0x28, NULL, 0);
3204 
3205         /* Last Position and Velocity Request (0x37)
3206          * returns 0x57 and (0x42, 0x4a, 0x83, or 0x84) and (0x43 or 0x56)  */
3207         (void)tsip_write(session, 0x37, NULL, 0);
3208         putbyte(buf, 0, 0x15);
3209         (void)tsip_write(session, 0x8e, buf, 1);
3210 
3211         /* Primary Receiver Configuration Parameters Request (0xbb-00)
3212          * returns  Primary Receiver Configuration Block (0xbb-00) */
3213         putbyte(buf, 0, 0x00);
3214         (void)tsip_write(session, 0xbb, buf, 1);
3215 }
3216 
3217 /* configure Acutime Gold to a known state */
configuration_packets_acutime_gold(struct gps_device_t * session)3218 void configuration_packets_acutime_gold(struct gps_device_t *session)
3219 {
3220         unsigned char buf[100];
3221 
3222         GPSD_LOG(LOG_PROG, &session->context->errout,
3223                  "TSIP: configuration_packets_acutime_gold()\n");
3224 
3225         /* Request Firmware Version (0x1c-01)
3226          * returns Firmware component version information (0x1x-81) */
3227         putbyte(buf, 0, 0x01);
3228         (void)tsip_write(session, 0x1c, buf, 1);
3229 
3230         /* Set Self-Survey Parameters (0x8e-a9) */
3231         putbyte(buf, 0, 0xa9); /* Subcode */
3232         putbyte(buf, 1, 0x01); /* Self-Survey Enable = enable */
3233         putbyte(buf, 2, 0x01); /* Position Save Flag = save position */
3234         putbe32(buf, 3, 2000); /* Self-Survey Length = 2000 fixes */
3235         /* Horizontal Uncertainty, 1-100, 1=best, 100=worst,
3236          *    default 100 */
3237         putbe32(buf, 7, 0);
3238         /* Verical Uncertainty, 1-100, 1=best, 100=worst,
3239          *    default 100
3240          * not present in RES SMT 360 */
3241         (void)tsip_write(session, 0x8e, buf, 11);
3242 
3243         /* Set PPS Output Option (0x8e-4e) */
3244         putbyte(buf, 0, 0x4e); /* Subcode */
3245         /* PPS driver switch = 2 (PPS is always output) */
3246         putbyte(buf, 1, 2);
3247         (void)tsip_write(session, 0x8e, buf, 2);
3248 
3249         /* Set Primary Receiver Configuration (0xbb-00) */
3250         putbyte(buf, 0, 0x00);   /* Subcode */
3251         /* Receiver mode, 7 = Force Overdetermined clock */
3252         putbyte(buf, 1, 0x07);
3253         /* Not enabled = unchanged
3254          * must be 0xff on RES SMT 360 */
3255         putbyte(buf, 2, 0xff);
3256         /* Dynamics code = default
3257          * must be 0xff on RES SMT 360 */
3258         putbyte(buf, 3, 0x01);
3259         /* Solution Mode = default
3260          * must be 0xff on RES SMT 360 */
3261         putbyte(buf, 4, 0x01);
3262         /* Elevation Mask = 10 deg */
3263         putbef32((char *)buf, 5, (float)10.0 * DEG_2_RAD);
3264         /* AMU Mask. 0 to 55. default is 4.0 */
3265         putbef32((char *)buf, 9, (float)4.0);
3266         /* PDOP Mask = 8.0, default = 6 */
3267         putbef32((char *)buf, 13, (float)8.0);
3268         /* PDOP Switch = 6.0, ignored in RES SMT 360 */
3269         putbef32((char *)buf, 17, (float)6.0);
3270         /* must be 0xff */
3271         putbyte(buf, 21, 0xff);
3272         /* Anti-Jam Mode, 0=Off, 1=On */
3273         putbyte(buf, 22, 0x0);
3274         /* Reserved.  Must be 0xffff */
3275         putbe16(buf, 23, 0xffff);
3276         /* Measurement Rate and Position Fix Rate = default
3277          * must be 0xffff on res smt 360 */
3278         putbe16(buf, 25, 0x0000);
3279         /* 27 is Constellation on RES SMT 360.
3280          * 1 = GPS, 2=GLONASS, 8=BeiDou, 0x10=Galileo, 5=QZSS */
3281         putbe32(buf, 27, 0xffffffff); /* Reserved */
3282         putbe32(buf, 31, 0xffffffff); /* Reserved */
3283         putbe32(buf, 35, 0xffffffff); /* Reserved */
3284         putbe32(buf, 39, 0xffffffff); /* Reserved */
3285         (void)tsip_write(session, 0xbb, buf, 43);
3286 
3287         /* Set Packet Broadcast Mask (0x8e-a5) */
3288         putbyte(buf, 0, 0xa5); /* Subcode */
3289         /* Packets bit field = default + Primary timing,
3290          *  Supplemental timing 32e1
3291          *  1=0x8f-ab, 4=0x8f-ac, 0x40=Automatic Output Packets */
3292         putbe16(buf, 1, 0x32e1);
3293         putbyte(buf, 3, 0x00); /* not used */
3294         putbyte(buf, 4, 0x00); /* not used */
3295         (void)tsip_write(session, 0x8e, buf, 5);
3296 }
3297 
3298 /* configure RES 360 to a known state */
configuration_packets_res360(struct gps_device_t * session)3299 void configuration_packets_res360(struct gps_device_t *session)
3300 {
3301     unsigned char buf[100];
3302 
3303     GPSD_LOG(LOG_PROG, &session->context->errout,
3304              "TSIP: configuration_packets_res360()\n");
3305 
3306     // should already have versions 0x8f-81 and 0x8f-83.
3307     /* Self-Survey Parameters (0x8e-a9) is default on
3308      * query them? */
3309 
3310     /* PPS Output Option (0x8e-4e) is default on */
3311 
3312     /* Set Packet Broadcast Mask (0x8e-a5)
3313      * RES SMT 360 default 5, 0 */
3314     putbyte(buf, 0, 0xa5); /* Subcode */
3315     /* Packets bit field = default + Auto output packets
3316      *  1=0x8f-ab, 4=0x8f-ac, 0x40=Automatic Output Packets */
3317     putbe16(buf, 1, 0x0045);
3318     putbe16(buf, 3, 0x0000);
3319     (void)tsip_write(session, 0x8e, buf, 5);
3320 
3321     // set I/O Options
3322     // RES SMT 360 defaults:  12 02 00 08
3323     // position and velocity only sent during self-survey.
3324     // Position
3325     putbyte(buf, 0, IO1_DP|IO1_LLA|IO1_ECEF);
3326     // Velocity
3327     putbyte(buf, 1, IO2_VECEF|IO2_ENU);
3328     // Timing
3329     putbyte(buf, 2, 0x01);          // Use 0x8e-a2
3330     // Auxillary
3331     putbyte(buf, 3, 0x08);          // Packet 0x5a off, dBHz
3332     (void)tsip_write(session, 0x35, buf, 4);
3333 
3334 #ifdef __UNUSED__
3335     // request I/O Options (0x55)
3336     (void)tsip_write(session, 0x35, buf, 0);
3337 
3338     // request Receiver Configuration (0xbb)
3339     putbyte(buf, 0, 0x00);
3340     (void)tsip_write(session, 0xbb, buf, 1);
3341     // Restart Self-Survey (0x8e-a6)
3342     // which gives us 2,000 normal fixes, before going quiet again.
3343     putbyte(buf, 0, 0xa6);
3344     putbyte(buf, 1, 0x00);
3345     (void)tsip_write(session, 0x8e, buf, 2);
3346 #endif // __UNUSED__
3347 }
3348 
3349 /* this is everything we export */
3350 /* *INDENT-OFF* */
3351 const struct gps_type_t driver_tsip =
3352 {
3353     .type_name      = "Trimble TSIP",   /* full name of type */
3354     .packet_type    = TSIP_PACKET,      /* associated lexer packet type */
3355     .flags          = DRIVER_STICKY,    /* remember this */
3356     .trigger        = NULL,             /* no trigger */
3357     .channels       = TSIP_CHANNELS,    /* consumer-grade GPS */
3358     .probe_detect   = tsip_detect,      /* probe for 9600O81 device */
3359     .get_packet     = generic_get,      /* use the generic packet getter */
3360     .parse_packet   = tsip_parse_input, /* parse message packets */
3361     .rtcm_writer    = NULL,             /* doesn't accept DGPS corrections */
3362     .init_query     = tsip_init_query,  /* non-perturbing initial query */
3363     .event_hook     = tsip_event_hook,  /* fire on various lifetime events */
3364 #ifdef RECONFIGURE_ENABLE
3365     .speed_switcher = tsip_speed_switch,/* change baud rate */
3366     .mode_switcher  = tsip_mode,        /* there is a mode switcher */
3367     .rate_switcher  = NULL,             /* no rate switcher */
3368     .min_cycle.tv_sec  = 1,             /* not relevant, no rate switch */
3369     .min_cycle.tv_nsec = 0,             /* not relevant, no rate switch */
3370 #endif /* RECONFIGURE_ENABLE */
3371 #ifdef CONTROLSEND_ENABLE
3372     .control_send   = tsip_control_send,/* how to send commands */
3373 #endif /* CONTROLSEND_ENABLE */
3374     .time_offset     = NULL,
3375 };
3376 /* *INDENT-ON* */
3377 
3378 #endif /* TSIP_ENABLE */
3379