2 * This file is Copyright (c) 2010 by the GPSD project
3 * BSD terms apply: see the file COPYING in the distribution root for details.
7 #include "gpsd_config.h"
16 #endif /* S_SPLINT_S */
22 #if defined(SUPERSTAR2_ENABLE) && defined(BINARY_ENABLE)
24 #include "driver_superstar2.h"
27 * These routines are specific to this driver
30 static gps_mask_t superstar2_parse_input(struct gps_device_t *);
31 static gps_mask_t superstar2_dispatch(struct gps_device_t *,
32 unsigned char *, size_t);
33 static gps_mask_t superstar2_msg_ack(struct gps_device_t *,
34 unsigned char *, size_t);
35 static gps_mask_t superstar2_msg_navsol_lla(struct gps_device_t *,
36 unsigned char *, size_t);
37 static gps_mask_t superstar2_msg_timing(struct gps_device_t *,
38 unsigned char *, size_t);
39 static gps_mask_t superstar2_msg_svinfo(struct gps_device_t *,
40 unsigned char *, size_t);
41 static gps_mask_t superstar2_msg_iono_utc(struct gps_device_t *,
42 unsigned char *, size_t);
43 static gps_mask_t superstar2_msg_ephemeris(struct gps_device_t *,
44 unsigned char *, size_t);
47 * These methods may be called elsewhere in gpsd
49 static ssize_t superstar2_control_send(struct gps_device_t *, char *, size_t);
50 static void superstar2_event_hook(struct gps_device_t *, event_t);
51 static bool superstar2_set_speed(struct gps_device_t *, speed_t, char, int);
52 static void superstar2_set_mode(struct gps_device_t *, int);
53 static ssize_t superstar2_write(struct gps_device_t *, char *, size_t);
57 * Decode the message ACK message
60 superstar2_msg_ack(struct gps_device_t *session UNUSED,
61 unsigned char *buf, size_t data_len)
66 "ACK %d %d %d %d %d\n",
67 buf[5], buf[6], buf[7], buf[8], buf[9]);
72 * Decode the navigation solution message. The ECEF version is intentionally
73 * unhandled. By suppressing evaluation of it, we gain the desirable feature
74 * that the fix update is atomic and exactly once per cycle.
79 superstar2_msg_navsol_lla(struct gps_device_t *session,
80 unsigned char *buf, size_t data_len)
85 union long_double l_d;
92 gpsd_report(LOG_PROG, "superstar2 #20 - user navigation data\n");
96 flags = (unsigned char)getub(buf, 72);
97 if ((flags & 0x0f) != 0x03) /* mode 3 is navigation */
101 /* extract time data */
102 (void)memset(&tm, '\0', sizeof(tm));
103 tm.tm_hour = (int)getub(buf, 4) & 0x1f;
104 tm.tm_min = (int)getub(buf, 5);
107 tm.tm_mday = (int)getub(buf, 14);
108 tm.tm_mon = (int)getub(buf, 15) - 1;
109 tm.tm_year = (int)getleuw(buf, 16) - 1900;
110 session->newdata.time = timegm(&tm) + (d - tm.tm_sec);
113 /* extract the local tangential plane (ENU) solution */
114 session->newdata.latitude = getled(buf, 18) * RAD_2_DEG;
115 session->newdata.longitude = getled(buf, 26) * RAD_2_DEG;
116 session->newdata.altitude = getlef(buf, 34);
117 session->newdata.speed = getlef(buf, 38);
118 session->newdata.track = getlef(buf, 42) * RAD_2_DEG;
119 session->newdata.climb = getlef(buf, 54);
120 mask |= LATLON_IS | ALTITUDE_IS | SPEED_IS | TRACK_IS | CLIMB_IS;
122 session->gpsdata.satellites_used = (int)getub(buf, 71) & 0x0f;
123 /*@i3@*/ session->gpsdata.dop.hdop = getleuw(buf, 66) * 0.1;
124 /*@i3@*/ session->gpsdata.dop.vdop = getleuw(buf, 68) * 0.1;
125 /* other DOP if available */
126 mask |= DOP_IS | USED_IS;
128 flags = (unsigned char)getub(buf, 70);
129 switch (flags & 0x1f) {
131 session->newdata.mode = MODE_3D;
132 session->gpsdata.status = STATUS_FIX;
135 session->newdata.mode = MODE_3D;
136 session->gpsdata.status = STATUS_DGPS_FIX;
139 session->newdata.mode = MODE_2D;
140 session->gpsdata.status = STATUS_DGPS_FIX;
144 session->newdata.mode = MODE_2D;
145 session->gpsdata.status = STATUS_FIX;
148 session->gpsdata.status = STATUS_NO_FIX;
149 session->newdata.mode = MODE_NO_FIX;
152 mask |= MODE_IS | STATUS_IS;
153 gpsd_report(LOG_DATA,
154 "NAVSOL_LLA: time=%.2f lat=%.2f lon=%.2f alt=%.2f track=%.2f speed=%.2f climb=%.2f mode=%d status=%d hdop=%.2f hdop=%.2f used=%d mask=%s\n",
155 session->newdata.time,
156 session->newdata.latitude,
157 session->newdata.longitude,
158 session->newdata.altitude,
159 session->newdata.track,
160 session->newdata.speed,
161 session->newdata.climb,
162 session->newdata.mode,
163 session->gpsdata.status,
164 session->gpsdata.dop.hdop,
165 session->gpsdata.dop.vdop,
166 session->gpsdata.satellites_used, gpsd_maskdump(mask));
174 superstar2_msg_svinfo(struct gps_device_t *session,
175 unsigned char *buf, size_t data_len)
177 int i, st, nchan, nsv;
182 gpsd_report(LOG_PROG, "superstar2 #33 - satellite data\n");
185 gpsd_zero_satellites(&session->gpsdata);
186 nsv = 0; /* number of actually used satellites */
187 for (i = st = 0; i < nchan; i++) {
188 /* get info for one channel/satellite */
191 if ((porn = (unsigned int)getub(buf, off) & 0x1f) == 0)
192 porn = (unsigned int)(getub(buf, off + 3) >> 1) + 87;
194 session->gpsdata.PRN[i] = (int)porn;
195 session->gpsdata.ss[i] = (float)getub(buf, off + 4);
196 session->gpsdata.elevation[i] = (int)getsb(buf, off + 1);
197 session->gpsdata.azimuth[i] = (unsigned short)getub(buf, off + 2) +
198 ((unsigned short)(getub(buf, off + 3) & 0x1) << 1);
201 if ((getub(buf, off) & 0x60) == 0x60)
202 session->gpsdata.used[nsv++] = session->gpsdata.PRN[i];
205 if (session->gpsdata.PRN[i])
208 session->gpsdata.skyview_time = NAN;
209 session->gpsdata.satellites_used = nsv;
210 session->gpsdata.satellites_visible = st;
211 gpsd_report(LOG_DATA,
212 "SVINFO: visible=%d used=%d mask={SATELLITE|USED}\n",
213 session->gpsdata.satellites_visible,
214 session->gpsdata.satellites_used);
215 return SATELLITE_IS | USED_IS;
219 superstar2_msg_version(struct gps_device_t *session,
220 unsigned char *buf, size_t data_len)
223 char main_sw[SZ], hw_part[SZ], boot_sw[SZ], ser_num[SZ];
226 /* byte 98 is device type, value = 3 means superstar2 */
227 if ((data_len != 101) || ((getub(buf, 98) & 0x0f) != 3))
231 (void)snprintf(main_sw, 15, "%s", (char *)buf + 4);
232 (void)snprintf(hw_part, 15, "%s", (char *)buf + 18);
233 (void)snprintf(boot_sw, 15, "%s", (char *)buf + 36);
234 (void)snprintf(ser_num, 14, "%s", (char *)buf + 73);
236 gpsd_report(LOG_PROG,
238 "hw part %s boot sw %s main sw %s ser num %s\n",
239 hw_part, boot_sw, main_sw, ser_num);
240 (void)strlcpy(session->subtype, main_sw, sizeof(session->subtype));
241 gpsd_report(LOG_DATA, "VERSION: subtype='%s' mask={DEVEICEID}\n",
250 superstar2_msg_timing(struct gps_device_t *session, unsigned char *buf,
254 union long_double l_d;
261 gpsd_report(LOG_PROG, "superstar2 #113 - timing status\n");
263 if ((getub(buf, 55) & 0x30) != 0)
267 /* extract time data */
268 (void)memset(&tm, '\0', sizeof(tm));
269 tm.tm_mday = (int)getsb(buf, 37);
270 tm.tm_mon = (int)getsb(buf, 38) - 1;
271 tm.tm_year = (int)getlesw(buf, 39) - 1900;
273 tm.tm_hour = (int)getsb(buf, 41);
274 tm.tm_min = (int)getsb(buf, 42);
277 session->newdata.time = timegm(&tm);
278 session->context->leap_seconds = (int)getsb(buf, 20);
281 gpsd_report(LOG_DATA, "TIMING: time=%.2f mask={TIME}\n",
282 session->newdata.time);
290 superstar2_msg_measurement(struct gps_device_t *session, unsigned char *buf,
291 size_t data_len UNUSED)
298 union long_double l_d;
300 gpsd_report(LOG_PROG, "superstar2 #23 - measurement block\n");
302 n = (int)getub(buf, 6); /* number of measurements */
303 if ((n < 1) || (n > MAXCHANNELS)) {
304 gpsd_report(LOG_INF, "too many measurements\n");
307 t = getled(buf, 7); /* measurement time */
308 for (i = 0; i < n; i++) {
309 session->gpsdata.raw.mtime[i] = t;
310 session->gpsdata.PRN[i] = (int)getub(buf, 11 * i + 15) & 0x1f;
311 session->gpsdata.ss[i] = (double)getub(buf, 11 * i * 15 + 1) / 4.0;
312 session->gpsdata.raw.codephase[i] =
313 (double)getleul(buf, 11 * i * 15 + 2);
314 ul = (unsigned long)getleul(buf, 11 * i * 15 + 6);
316 session->gpsdata.raw.satstat[i] = (unsigned int)(ul & 0x03L);
317 session->gpsdata.raw.carrierphase[i] = (double)((ul >> 2) & 0x03ffL);
318 session->gpsdata.raw.pseudorange[i] = (double)(ul >> 12);
322 #endif /* RAW_ENABLE */
326 /* request for ionospheric and utc time data #75 */
328 static unsigned char iono_utc_msg[] = { 0x01, 0x4b, 0xb4, 0x00, 0x00, 0x01 };
334 * Ionospheric/UTC parameters
337 superstar2_msg_iono_utc(struct gps_device_t *session, unsigned char *buf,
338 size_t data_len UNUSED)
342 i = (unsigned int)getub(buf, 12);
343 u = (unsigned int)getub(buf, 21);
344 gpsd_report(LOG_PROG,
345 "superstar2 #75 - ionospheric & utc data: iono %s utc %s\n",
346 i ? "ok" : "bad", u ? "ok" : "bad");
347 session->driver.superstar2.last_iono = time(NULL);
357 superstar2_msg_ephemeris(struct gps_device_t *session, unsigned char *buf,
358 size_t data_len UNUSED)
361 prn = (unsigned int)(getub(buf, 4) & 0x1f);
362 gpsd_report(LOG_PROG, "superstar2 #22 - ephemeris data - prn %u\n", prn);
364 /* ephemeris data updates fairly slowly, but when it does, poll UTC */
365 if ((time(NULL) - session->driver.superstar2.last_iono) > 60)
366 (void)superstar2_write(session, (char *)iono_utc_msg,
367 sizeof(iono_utc_msg));
374 superstar2_write(struct gps_device_t *session, char *msg, size_t msglen)
376 unsigned short c = 0;
379 for (i = 0; i < (ssize_t) (msglen - 2); i++)
380 c += (unsigned short)msg[i];
382 msg[(int)msg[3] + 4] = (char)((c >> 8) & 0xff);
383 msg[(int)msg[3] + 5] = (char)(c & 0xff);
384 gpsd_report(LOG_IO, "writing superstar2 control type %d len %zu:%s\n",
385 (int)msg[1] & 0x7f, msglen,
386 gpsd_hexdump_wrapper(msg, msglen, LOG_IO));
387 return (i = gpsd_write(session, msg, msglen));
391 * Parse the data from the device
394 superstar2_dispatch(struct gps_device_t * session, unsigned char *buf,
402 type = (int)buf[SUPERSTAR2_TYPE_OFFSET];
403 (void)snprintf(session->gpsdata.tag,
404 sizeof(session->gpsdata.tag), "SS2-%d", type);
406 session->cycle_end_reliable = true;
409 case SUPERSTAR2_ACK: /* Message Acknowledgement */
410 return superstar2_msg_ack(session, buf, len);
411 case SUPERSTAR2_SVINFO: /* Satellite Visibility Data */
412 return superstar2_msg_svinfo(session, buf, len);
413 case SUPERSTAR2_NAVSOL_LLA: /* Navigation Data */
414 return superstar2_msg_navsol_lla(session, buf,
415 len) | (CLEAR_IS | REPORT_IS);
416 case SUPERSTAR2_VERSION: /* Hardware/Software Version */
417 return superstar2_msg_version(session, buf, len);
418 case SUPERSTAR2_TIMING: /* Timing Parameters */
419 return superstar2_msg_timing(session, buf, len);
420 case SUPERSTAR2_MEASUREMENT: /* Timing Parameters */
421 return superstar2_msg_measurement(session, buf, len);
422 case SUPERSTAR2_IONO_UTC:
423 return superstar2_msg_iono_utc(session, buf, len);
424 case SUPERSTAR2_EPHEMERIS:
425 return superstar2_msg_ephemeris(session, buf, len);
428 gpsd_report(LOG_WARN,
429 "unknown superstar2 packet id 0x%02x length %zd: %s\n",
430 type, len, gpsd_hexdump_wrapper(buf, len, LOG_WARN));
435 /**********************************************************
437 * Externally called routines below here
439 **********************************************************/
441 /* canned config messages */
442 /* Initiate Link ID# 63 */
443 static unsigned char link_msg[] = { 0x01, 0x3f, 0xc0, 0x08,
444 0x55, 0x47, 0x50, 0x53, 0x2d, 0x30, 0x30, 0x30,
448 /* Request Hardware/Software Identification ID# 45 */
449 static unsigned char version_msg[] = { 0x01, 0x2d, 0xd2, 0x00, 0x00, 0x01 };
453 static void superstar2_event_hook(struct gps_device_t *session, event_t event)
455 if (event == event_wakeup) {
456 (void)superstar2_write(session, (char *)link_msg, sizeof(link_msg));
457 (void)usleep(320000);
458 (void)superstar2_write(session, (char *)version_msg,
459 sizeof(version_msg));
463 /* query firmware version */
464 if (event == event_identified)
465 (void)superstar2_write(session, (char *)version_msg,
466 sizeof(version_msg));
468 /* FIX-ME: check to see if this really needs to be resent on reactivation */
469 if (event == event_identified || event == event_reactivate) {
471 unsigned char svinfo_msg[] = { 0x01, 0xa1, 0x5e, 0x00, 0x00, 0x01 };
472 unsigned char timing_msg[] = { 0x01, 0xf1, 0x0e, 0x00, 0x00, 0x01 };
473 unsigned char navsol_lla_msg[] =
474 { 0x01, 0x94, 0x6b, 0x00, 0x00, 0x01 };
475 unsigned char ephemeris_msg[] =
476 { 0x01, 0x96, 0x69, 0x00, 0x00, 0x01 };
477 unsigned char measurement_msg[] =
478 { 0x01, 0x97, 0x68, 0x01, 0x00, 0x01, 0x01 };
481 (void)superstar2_write(session, (char *)timing_msg,
483 (void)superstar2_write(session, (char *)measurement_msg,
484 sizeof(measurement_msg));
485 (void)superstar2_write(session, (char *)svinfo_msg,
487 (void)superstar2_write(session, (char *)navsol_lla_msg,
488 sizeof(navsol_lla_msg));
489 (void)superstar2_write(session, (char *)version_msg,
490 sizeof(version_msg));
491 (void)superstar2_write(session, (char *)ephemeris_msg,
492 sizeof(ephemeris_msg));
493 (void)superstar2_write(session, (char *)iono_utc_msg,
494 sizeof(iono_utc_msg));
495 session->driver.superstar2.last_iono = time(NULL);
500 * This is the entry point to the driver. When the packet sniffer recognizes
501 * a packet for this driver, it calls this method which passes the packet to
502 * the binary processor or the nmea processor, depending on the session type.
504 static gps_mask_t superstar2_parse_input(struct gps_device_t *session)
508 if (session->packet.type == SUPERSTAR2_PACKET) {
509 st = superstar2_dispatch(session, session->packet.outbuffer,
510 session->packet.length);
511 session->gpsdata.dev.driver_mode = MODE_BINARY;
514 } else if (session->packet.type == NMEA_PACKET) {
515 st = nmea_parse((char *)session->packet.outbuffer, session);
516 (void)gpsd_switch_driver(session, "Generic NMEA");
517 session->gpsdata.dev.driver_mode = MODE_NMEA;
519 #endif /* NMEA_ENABLE */
524 #ifdef ALLOW_CONTROLSEND
526 superstar2_control_send(struct gps_device_t *session, char *msg,
529 /*@ +charint -mayaliasunique @*/
530 session->msgbuf[0] = 0x1; /* SOH */
531 session->msgbuf[1] = msg[0];
532 session->msgbuf[2] = msg[0] ^ 0xff;
533 session->msgbuf[3] = (char)(msglen + 1);
534 (void)memcpy(session->msgbuf + 4, msg + 1, msglen - 1);
535 session->msgbuflen = (size_t) (msglen + 5);
536 /*@ -charint +mayaliasunique @*/
537 return superstar2_write(session, session->msgbuf, session->msgbuflen);
539 #endif /* ALLOW_CONTROLSEND */
541 #ifdef ALLOW_RECONFIGURE
542 static bool superstar2_set_speed(struct gps_device_t *session,
543 speed_t speed, char parity, int stopbits)
545 /* parity and stopbit switching aren't available on this chip */
546 if (parity != session->gpsdata.dev.parity
547 || stopbits != (int)session->gpsdata.dev.stopbits) {
551 unsigned char speed_msg[] =
552 { 0x01, 0x48, 0xB7, 0x01, 0x00, 0x00, 0x00 };
554 /* high bit 0 in the mode word means set NMEA mode */
555 speed_msg[4] = (unsigned char)(speed / 300);
557 return (superstar2_write(session, (char *)speed_msg, 7) == 7);
560 #endif /* ALLOW_RECONFIGURE */
562 static void superstar2_set_mode(struct gps_device_t *session, int mode)
564 if (mode == MODE_NMEA) {
566 unsigned char mode_msg[] =
567 { 0x01, 0x48, 0xB7, 0x01, 0x00, 0x00, 0x00 };
569 /* high bit 0 in the mode word means set NMEA mode */
570 mode_msg[4] = (unsigned char)(session->gpsdata.dev.baudrate / 300);
571 (void)superstar2_write(session, (char *)mode_msg, 7);
574 session->back_to_nmea = false;
579 const struct gps_type_t superstar2_binary = {
580 /* Full name of type */
581 .type_name = "SuperStarII binary",
582 /* Associated lexer packet type */
583 .packet_type = SUPERSTAR2_PACKET,
584 /* Response string that identifies device (not active) */
586 /* Number of satellite channels supported by the device */
588 /* Startup-time device detector */
589 .probe_detect = NULL,
590 /* Packet getter (using default routine) */
591 .get_packet = generic_get,
592 /* Parse message packets */
593 .parse_packet = superstar2_parse_input,
594 /* RTCM handler (using default routine) */
595 .rtcm_writer = pass_rtcm,
596 /* Fire on various lifetime events */
597 .event_hook = superstar2_event_hook,
598 #ifdef ALLOW_RECONFIGURE
599 /* Speed (baudrate) switch */
600 .speed_switcher = superstar2_set_speed,
601 /* Switch to NMEA mode */
602 .mode_switcher = superstar2_set_mode,
603 /* Message delivery rate switcher (not active) */
604 .rate_switcher = NULL,
605 /* Minimum cycle time (not used) */
607 #endif /* ALLOW_RECONFIGURE */
608 #ifdef ALLOW_CONTROLSEND
609 /* Control string sender - should provide checksum and trailer */
610 .control_send = superstar2_control_send,
611 #endif /* ALLOW_CONTROLSEND */
613 .ntp_offset = NULL, /* no method for NTP fudge factor */
614 #endif /* NTPSHM_ ENABLE */
617 #endif /* defined(SUPERSTAR2_ENABLE) && defined(BINARY_ENABLE) */