summaryrefslogtreecommitdiff
path: root/driver_superstar2.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver_superstar2.c')
-rw-r--r--driver_superstar2.c528
1 files changed, 528 insertions, 0 deletions
diff --git a/driver_superstar2.c b/driver_superstar2.c
new file mode 100644
index 00000000..34d86589
--- /dev/null
+++ b/driver_superstar2.c
@@ -0,0 +1,528 @@
+/* $Id$ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdio.h>
+
+#include "gpsd_config.h"
+#include "gpsd.h"
+
+#if defined(SUPERSTAR2_ENABLE) && defined(BINARY_ENABLE)
+#include "bits.h"
+#include "driver_superstar2.h"
+
+/*
+ * These routines are specific to this driver
+ */
+
+static gps_mask_t superstar2_parse_input(struct gps_device_t *);
+static gps_mask_t superstar2_dispatch(struct gps_device_t *,
+ unsigned char *, size_t );
+static gps_mask_t superstar2_msg_ack(struct gps_device_t *,
+ unsigned char *, size_t );
+static gps_mask_t superstar2_msg_navsol_lla(struct gps_device_t *,
+ unsigned char *, size_t );
+static gps_mask_t superstar2_msg_navsol_ecef(struct gps_device_t *,
+ unsigned char *, size_t );
+static gps_mask_t superstar2_msg_timing(struct gps_device_t *,
+ unsigned char *, size_t );
+static gps_mask_t superstar2_msg_svinfo(struct gps_device_t *,
+ unsigned char *, size_t );
+
+/*
+ * These methods may be called elsewhere in gpsd
+ */
+static ssize_t superstar2_control_send(struct gps_device_t *, char *, size_t );
+static void superstar2_probe_wakeup(struct gps_device_t *);
+static void superstar2_configurator(struct gps_device_t *, unsigned int );
+static bool superstar2_set_speed(struct gps_device_t *, speed_t, char, int);
+static void superstar2_set_mode(struct gps_device_t *, int );
+static void superstar2_probe_wakeup(struct gps_device_t *);
+static void superstar2_probe_subtype(struct gps_device_t *, unsigned int );
+
+/*
+ * Decode the message ACK message
+ */
+static gps_mask_t
+superstar2_msg_ack(struct gps_device_t *session UNUSED,
+ unsigned char *buf, size_t data_len)
+{
+ if (data_len == 11)
+ gpsd_report(LOG_PROG,
+ "superstar2 #126 - "
+ "ACK 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ buf[5], buf[6], buf[7], buf[8], buf[9]);
+ return ONLINE_SET; /* always returns ONLINE_SET, but avoid runt packets */
+}
+
+/*
+ * Decode the navigation solution message
+ */
+static gps_mask_t
+superstar2_msg_navsol_lla(struct gps_device_t *session,
+ unsigned char *buf, size_t data_len)
+{
+ gps_mask_t mask;
+ unsigned char flags;
+ union int_float i_f;
+ union long_double l_d;
+ double d;
+ struct tm tm;
+
+ if (data_len != 77)
+ return 0;
+
+ gpsd_report(LOG_PROG, "superstar2 #20 - user navigation data\n");
+ mask = ONLINE_SET;
+
+ flags = getub(buf, 72);
+ if ((flags & 0x0f) != 3) /* mode 3 is navigation */
+ return mask;
+
+ /* extract time data */
+ bzero(&tm, sizeof(tm));
+ tm.tm_hour = getub(buf, 4) & 0x1f;
+ tm.tm_min = getub(buf, 5);
+ d = getled(buf, 6);
+ tm.tm_sec = (int)d;
+ tm.tm_mday = getub(buf, 14);
+ tm.tm_mon = getub(buf, 15) - 1;
+ tm.tm_year = getleuw(buf, 16) - 1900;
+ session->gpsdata.fix.time = session->gpsdata.sentence_time =
+ timegm(&tm) + (d - tm.tm_sec);
+ mask |= TIME_SET;
+
+ /* extract the local tangential plane (ENU) solution */
+ session->gpsdata.fix.latitude = getled(buf,18) * RAD_2_DEG;
+ session->gpsdata.fix.longitude = getled(buf,26) * RAD_2_DEG;
+ session->gpsdata.fix.altitude = getlef(buf,34);
+ session->gpsdata.fix.speed = getlef(buf,38);
+ session->gpsdata.fix.track = getlef(buf,42) * RAD_2_DEG;
+ session->gpsdata.fix.climb = getlef(buf,54);
+ mask |= LATLON_SET | ALTITUDE_SET | SPEED_SET | TRACK_SET | CLIMB_SET ;
+
+ session->gpsdata.satellites_used = getub(buf,71) & 0x0f;
+ session->gpsdata.hdop = getleuw(buf,66) * 0.1;
+ session->gpsdata.vdop = getleuw(buf,68) * 0.1;
+ /* other DOP if available */
+ mask |= HDOP_SET | VDOP_SET | USED_SET;
+
+ flags = getub(buf,70);
+ switch (flags & 0x1f) {
+ case 2:
+ session->gpsdata.fix.mode = MODE_3D;
+ session->gpsdata.status = STATUS_FIX;
+ break;
+ case 4:
+ session->gpsdata.fix.mode = MODE_3D;
+ session->gpsdata.status = STATUS_DGPS_FIX;
+ break;
+ case 5:
+ session->gpsdata.fix.mode = MODE_2D;
+ session->gpsdata.status = STATUS_DGPS_FIX;
+ break;
+ case 3:
+ case 6:
+ session->gpsdata.fix.mode = MODE_2D;
+ session->gpsdata.status = STATUS_FIX;
+ break;
+ default:
+ session->gpsdata.status = STATUS_NO_FIX;
+ session->gpsdata.fix.mode = MODE_NO_FIX;
+ }
+
+ /* CYCLE_START_SET if this message starts a reporting period */
+ mask |= MODE_SET | STATUS_SET | CYCLE_START_SET ;
+
+ return mask;
+}
+
+static gps_mask_t
+superstar2_msg_navsol_ecef(struct gps_device_t *session,
+ unsigned char *buf, size_t data_len)
+{
+ gps_mask_t mask;
+ unsigned char flags;
+ union int_float i_f;
+ union long_double l_d;
+ double tm, tow;
+
+ if (data_len != 85)
+ return 0;
+
+ gpsd_report(LOG_PROG, "superstar2 #21 - ecef navigation data\n");
+ mask = ONLINE_SET;
+
+ flags = getub(buf, 79) & 0x1f;
+ if ((flags < 2) || (flags > 5))
+ return mask;
+
+ /* extract time data */
+ tow = getled(buf, 4);
+ session->driver.superstar2.gps_week = getleuw(buf, 12);
+ tm = gpstime_to_unix((int)session->driver.superstar2.gps_week, tow) -
+ session->context->leap_seconds;
+ session->gpsdata.fix.time = session->gpsdata.sentence_time = tm;
+ mask |= TIME_SET;
+
+ /* extract the earth-centered, earth-fixed (ECEF) solution */
+ ecef_to_wgs84fix(&session->gpsdata,
+ getled(buf, 14), getled(buf, 22), getled(buf, 30),
+ getlef(buf, 38), getlef(buf, 42), getlef(buf, 46));
+ mask |= LATLON_SET | ALTITUDE_SET | SPEED_SET | TRACK_SET | CLIMB_SET ;
+
+ session->gpsdata.satellites_used = getub(buf, 79) & 0x0f;
+ session->gpsdata.hdop = getleuw(buf, 74) * 0.1;
+ session->gpsdata.vdop = getleuw(buf, 76) * 0.1;
+ /* other DOP if available */
+ mask |= HDOP_SET | VDOP_SET | USED_SET;
+
+ flags = getub(buf,70);
+ switch (flags & 0x1f) {
+ case 2:
+ session->gpsdata.fix.mode = MODE_3D;
+ session->gpsdata.status = STATUS_FIX;
+ break;
+ case 4:
+ session->gpsdata.fix.mode = MODE_3D;
+ session->gpsdata.status = STATUS_DGPS_FIX;
+ break;
+ case 5:
+ session->gpsdata.fix.mode = MODE_2D;
+ session->gpsdata.status = STATUS_DGPS_FIX;
+ break;
+ case 3:
+ case 6:
+ session->gpsdata.fix.mode = MODE_2D;
+ session->gpsdata.status = STATUS_FIX;
+ break;
+ default:
+ session->gpsdata.status = STATUS_NO_FIX;
+ session->gpsdata.fix.mode = MODE_NO_FIX;
+ }
+
+ /* CYCLE_START_SET if this message starts a reporting period */
+ mask |= MODE_SET | STATUS_SET | CYCLE_START_SET;
+
+ return mask;
+}
+
+/**
+ * GPS Satellite Info
+ */
+static gps_mask_t
+superstar2_msg_svinfo(struct gps_device_t *session,
+ unsigned char *buf, size_t data_len)
+{
+ unsigned char i, st, nchan, nsv;
+
+ if (data_len != 67)
+ return 0;
+
+ gpsd_report(LOG_PROG, "superstar2 #33 - satellite data");
+
+ nchan = 12;
+ gpsd_zero_satellites(&session->gpsdata);
+ nsv = 0; /* number of actually used satellites */
+ for (i = st = 0; i < nchan; i++) {
+ /* get info for one channel/satellite */
+ int off = i*5 + 5;
+ int porn;
+ if ((porn = getub(buf, off) & 0x1f) == 0)
+ porn = (getub(buf, off+3) >> 1) + 87;
+
+ session->gpsdata.PRN[i] = porn;
+ session->gpsdata.ss[i] = getub(buf, off+4);
+ session->gpsdata.elevation[i] = getsb(buf, off+1);
+ session->gpsdata.azimuth[i] = (unsigned short)getub(buf, off+2) + ((unsigned short)(getub(buf, off+3) & 0x1) << 1);
+
+ if ((getub(buf, off) & 0x60) == 0x60)
+ session->gpsdata.used[nsv++] = session->gpsdata.PRN[i];
+
+ if(session->gpsdata.PRN[i])
+ st++;
+ }
+ session->gpsdata.satellites_used = nsv;
+ session->gpsdata.satellites = st;
+ return SATELLITE_SET | USED_SET | ONLINE_SET;
+}
+
+static gps_mask_t
+superstar2_msg_version(struct gps_device_t *session,
+ unsigned char *buf, size_t data_len)
+{
+#define SZ 16
+ char main_sw[SZ], hw_part[SZ], boot_sw[SZ], ser_num[SZ];
+
+ /* byte 98 is device type, value = 3 means superstar2 */
+ if ((data_len != 101) || ((getub(buf,98) & 0x0f) != 3))
+ return 0;
+
+ snprintf(main_sw, 15, "%s", buf+4);
+ snprintf(hw_part, 15, "%s", buf+18);
+ snprintf(boot_sw, 15, "%s", buf+36);
+ snprintf(ser_num, 14, "%s", buf+73);
+
+ gpsd_report(LOG_PROG,
+ "superstar2 #45 - "
+ "hw part %s boot sw %s main sw %s ser num %s\n",
+ hw_part, boot_sw, main_sw, ser_num);
+ strlcpy(session->subtype, main_sw, sizeof(session->subtype));
+ return DEVICEID_SET | ONLINE_SET;
+}
+
+/**
+ * GPS Leap Seconds
+ */
+static gps_mask_t
+superstar2_msg_timing(struct gps_device_t *session, unsigned char *buf, size_t data_len)
+{
+ union long_double l_d;
+ double d;
+ struct tm tm;
+
+ if (data_len != 65)
+ return 0;
+
+ gpsd_report(LOG_PROG, "superstar2 #113 - timing status\n");
+ if ((getub(buf, 55) & 0x30) != 0)
+ return ONLINE_SET;
+
+ /* extract time data */
+ bzero(&tm, sizeof(tm));
+ tm.tm_mday = getsb(buf, 37);
+ tm.tm_mon = getsb(buf, 38) - 1;
+ tm.tm_year = getlesw(buf, 39) - 1900;
+
+ tm.tm_hour = getsb(buf, 41);
+ tm.tm_min = getsb(buf, 42);
+ d = getled(buf, 43);
+ tm.tm_sec = (int)d;
+ session->gpsdata.sentence_time = session->gpsdata.fix.time = timegm(&tm);
+ session->context->leap_seconds = getsb(buf,20);
+
+ return TIME_SET | ONLINE_SET;
+}
+
+
+/**
+ * Write data to the device, doing any required padding or checksumming
+ */
+static ssize_t
+superstar2_control_send(struct gps_device_t *session, char *msg, size_t msglen)
+{
+ unsigned short c = 0;
+ size_t i;
+
+ for (i = 0; i < msglen - 2; i++)
+ c += (unsigned char)msg[i];
+// c = htons(c); // XXX is this needed on big-endian machines?
+ memcpy(msg + msg[3] + 4, &c, 2);
+ gpsd_report(LOG_IO, "writing superstar2 control type %02x len %zu:%s\n",
+ (unsigned char)msg[1], msglen,
+ gpsd_hexdump_wrapper(msg, msglen, LOG_IO));
+ return gpsd_write(session, msg, msglen);
+}
+
+/**
+ * Parse the data from the device
+ */
+gps_mask_t
+superstar2_dispatch(struct gps_device_t *session, unsigned char *buf,
+ size_t len)
+{
+ int type;
+
+ if (len == 0)
+ return 0;
+
+ type = buf[SUPERSTAR2_TYPE_OFFSET];
+ (void)snprintf(session->gpsdata.tag,
+ sizeof(session->gpsdata.tag), "SS2-%u", (int)type);
+
+ switch (type)
+ {
+ case SUPERSTAR2_ACK: /* Message Acknowledgement */
+ return superstar2_msg_ack(session, buf, len);
+ case SUPERSTAR2_SVINFO: /* Satellite Visibility Data */
+ return superstar2_msg_svinfo(session, buf, len);
+ case SUPERSTAR2_NAVSOL_LLA: /* Navigation Data */
+ return superstar2_msg_navsol_lla(session, buf, len);
+ case SUPERSTAR2_NAVSOL_ECEF: /* Navigation Data */
+ return superstar2_msg_navsol_ecef(session, buf, len);
+ case SUPERSTAR2_VERSION: /* Hardware/Software Version */
+ return superstar2_msg_version(session, buf, len);
+ case SUPERSTAR2_TIMING: /* Timing Parameters */
+ return superstar2_msg_timing(session, buf, len);
+
+ default:
+ /* XXX This gets noisy in a hurry. */
+ gpsd_report(LOG_WARN,
+ "unknown superstar2 packet id 0x%02x length %zd: %s\n",
+ type, len, gpsd_hexdump_wrapper(buf, len, LOG_WARN));
+ return 0;
+ }
+}
+
+/**********************************************************
+ *
+ * Externally called routines below here
+ *
+ **********************************************************/
+/* canned config messages */
+/* Initiate Link ID# 63 */
+static unsigned char link_msg[] = {0x01, 0x3f, 0xc0, 0x08,
+ 0x55, 0x47, 0x50, 0x53, 0x2d, 0x30, 0x30, 0x30,
+ 0x00, 0x00};
+
+/* Request Hardware/Software Identification ID# 45 */
+static unsigned char version_msg[] = {0x01, 0x2d, 0xd2, 0x00, 0x00, 0x01};
+
+static void
+superstar2_probe_wakeup(struct gps_device_t *session)
+{
+ superstar2_control_send(session, link_msg, sizeof(link_msg));
+ usleep(300000);
+ superstar2_control_send(session, version_msg, sizeof(version_msg));
+ return;
+}
+
+static void
+superstar2_probe_subtype(struct gps_device_t *session,
+ unsigned int seq)
+{
+ if (seq == 0){
+ superstar2_control_send(session, link_msg, sizeof(link_msg));
+ usleep(300000);
+ superstar2_control_send(session, version_msg, sizeof(version_msg));
+ }
+ return;
+}
+
+static void superstar2_configurator(struct gps_device_t *session,
+ unsigned int seq UNUSED)
+{
+ unsigned char a;
+ unsigned char message_list[] = {
+ SUPERSTAR2_NAVSOL_LLA,
+ SUPERSTAR2_SVINFO,
+ SUPERSTAR2_TIMING,
+ SUPERSTAR2_NAVSOL_ECEF,
+ SUPERSTAR2_DUMMY};
+ unsigned char message2_list[] = {
+ SUPERSTAR2_MEASUREMENT,
+ SUPERSTAR2_DUMMY};
+ unsigned char tmpl_msg[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
+ unsigned char tmpl2_msg[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
+
+ for(a = 0; message_list[a] != 0; a++){
+ /* set high bit to enable continuous output */
+ tmpl_msg[1] = (unsigned char)(message_list[a] | 0x80);
+ tmpl_msg[2] = (unsigned char)(tmpl_msg[1] ^ 0xff);
+ superstar2_control_send(session, tmpl_msg, sizeof(tmpl_msg));
+ usleep(20000);
+ }
+ for(a = 0; message2_list[a] != 0; a++){
+ /* set high bit to enable continuous output */
+ tmpl2_msg[1] = (unsigned char)(message2_list[a] | 0x80);
+ tmpl2_msg[2] = (unsigned char)(tmpl2_msg[1] ^ 0xff);
+ superstar2_control_send(session, tmpl2_msg, sizeof(tmpl2_msg));
+ usleep(20000);
+ }
+ superstar2_control_send(session, version_msg, sizeof(version_msg));
+}
+
+/*
+ * This is the entry point to the driver. When the packet sniffer recognizes
+ * a packet for this driver, it calls this method which passes the packet to
+ * the binary processor or the nmea processor, depending on the session type.
+ */
+static gps_mask_t superstar2_parse_input(struct gps_device_t *session)
+{
+ gps_mask_t st;
+
+ if (session->packet.type == SUPERSTAR2_PACKET){
+ st = superstar2_dispatch(session, session->packet.outbuffer,
+ session->packet.length);
+ session->gpsdata.driver_mode = MODE_BINARY;
+ return st;
+#ifdef NMEA_ENABLE
+ } else if (session->packet.type == NMEA_PACKET) {
+ st = nmea_parse((char *)session->packet.outbuffer, session);
+ (void)gpsd_switch_driver(session, "Generic NMEA");
+ session->gpsdata.driver_mode = MODE_NMEA;
+ return st;
+#endif /* NMEA_ENABLE */
+ } else
+ return 0;
+}
+
+static bool superstar2_set_speed(struct gps_device_t *session,
+ speed_t speed, char parity, int stopbits)
+{
+ /* set port operating mode, speed, bits etc. here */
+ return false;
+}
+
+/*
+ * Switch between NMEA and binary mode, if supported
+ */
+static void superstar2_set_mode(struct gps_device_t *session, int mode)
+{
+ if (mode == MODE_NMEA) {
+ // superstar2_to_nmea(session->gpsdata.gps_fd,session->gpsdata.baudrate); /* send the mode switch control string */
+ } else {
+ session->back_to_nmea = false;
+ }
+}
+
+const struct gps_type_t superstar2_binary = {
+ /* Full name of type */
+ .type_name = "SuperStarII binary",
+ /* associated lexer packet type */
+ .packet_type = SUPERSTAR2_PACKET,
+ /* Response string that identifies device (not active) */
+ .trigger = NULL,
+ /* Number of satellite channels supported by the device */
+ .channels = 12,
+ /* Control string sender - should provide checksum and trailer */
+ .control_send = superstar2_control_send,
+ /* Startup-time device detector */
+ .probe_detect = NULL,
+ /* Wakeup to be done before each baud hunt */
+ .probe_wakeup = superstar2_probe_wakeup,
+ /* Initialize the device and get subtype */
+ .probe_subtype = superstar2_probe_subtype,
+#ifdef ALLOW_RECONFIGURE
+ /* Enable what reports we need */
+ .configurator = superstar2_configurator,
+#endif /* ALLOW_RECONFIGURE */
+ /* Packet getter (using default routine) */
+ .get_packet = generic_get,
+ /* Parse message packets */
+ .parse_packet = superstar2_parse_input,
+ /* RTCM handler (using default routine) */
+ .rtcm_writer = pass_rtcm,
+ /* Speed (baudrate) switch */
+ .speed_switcher = superstar2_set_speed,
+ /* Switch to NMEA mode */
+ .mode_switcher = superstar2_set_mode,
+ /* Message delivery rate switcher (not active) */
+ .rate_switcher = NULL,
+ /* Number of chars per report cycle (not active) */
+ .cycle_chars = -1,
+#ifdef ALLOW_RECONFIGURE
+ /* Undo the actions of .configurator */
+ .revert = NULL,
+#endif /* ALLOW_RECONFIGURE */
+ /* Puts device back to original settings */
+ .wrapup = NULL,
+ /* Number of updates per second */
+ .cycle = 1
+};
+#endif /* defined(SUPERSTAR2_ENABLE) && defined(BINARY_ENABLE) */