summaryrefslogtreecommitdiff
path: root/driver_ubx.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver_ubx.c')
-rw-r--r--driver_ubx.c688
1 files changed, 688 insertions, 0 deletions
diff --git a/driver_ubx.c b/driver_ubx.c
new file mode 100644
index 00000000..f3757aba
--- /dev/null
+++ b/driver_ubx.c
@@ -0,0 +1,688 @@
+/* $Id$
+ *
+ * UBX driver
+ */
+
+#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 <assert.h>
+
+#include "gpsd_config.h"
+#include "gpsd.h"
+#if defined(UBX_ENABLE) && defined(BINARY_ENABLE)
+#include "driver_ubx.h"
+
+#include "bits.h"
+
+/*
+ * A ubx packet looks like this:
+ * leader: 0xb5 0x62
+ * message class: 1 byte
+ * message type: 1 byte
+ * length of payload: 2 bytes
+ * payload: variable length
+ * checksum: 2 bytes
+ *
+ * see also the FV25 and UBX documents on reference.html
+ */
+
+static bool have_port_configuration = false;
+static unsigned char original_port_settings[20];
+static unsigned char sbas_in_use;
+
+ bool ubx_write(struct gps_device_t *session, unsigned int msg_class, unsigned int msg_id, unsigned char *msg, unsigned short data_len);
+ gps_mask_t ubx_parse(struct gps_device_t *session, unsigned char *buf, size_t len);
+ void ubx_catch_model(struct gps_device_t *session, unsigned char *buf, size_t len);
+static gps_mask_t ubx_msg_nav_sol(struct gps_device_t *session, unsigned char *buf, size_t data_len);
+static gps_mask_t ubx_msg_nav_dop(struct gps_device_t *session, unsigned char *buf, size_t data_len);
+static gps_mask_t ubx_msg_nav_timegps(struct gps_device_t *session, unsigned char *buf, size_t data_len);
+static gps_mask_t ubx_msg_nav_svinfo(struct gps_device_t *session, unsigned char *buf, size_t data_len);
+static void ubx_msg_sbas(unsigned char *buf);
+static void ubx_msg_inf(unsigned char *buf, size_t data_len);
+
+/**
+ * Navigation solution message
+ */
+static gps_mask_t
+ubx_msg_nav_sol(struct gps_device_t *session, unsigned char *buf, size_t data_len)
+{
+ unsigned short gw;
+ unsigned int tow, flags;
+ double epx, epy, epz, evx, evy, evz;
+ unsigned char navmode;
+ gps_mask_t mask;
+ double t;
+
+ if (data_len != 52)
+ return 0;
+
+ flags = (unsigned int)getub(buf, 11);
+ mask = ONLINE_SET;
+ if ((flags & (UBX_SOL_VALID_WEEK |UBX_SOL_VALID_TIME)) != 0){
+ tow = getleul(buf, 0);
+ gw = (unsigned short)getlesw(buf, 8);
+ session->driver.ubx.gps_week = gw;
+
+ t = gpstime_to_unix((int)session->driver.ubx.gps_week, tow/1000.0) - session->context->leap_seconds;
+ session->gpsdata.sentence_time = t;
+ session->gpsdata.fix.time = t;
+ mask |= TIME_SET;
+#ifdef NTPSHM_ENABLE
+ /* TODO overhead */
+ if (session->context->enable_ntpshm)
+ (void)ntpshm_put(session, session->gpsdata.sentence_time);
+#endif
+ }
+
+ epx = (double)(getlesl(buf, 12)/100.0);
+ epy = (double)(getlesl(buf, 16)/100.0);
+ epz = (double)(getlesl(buf, 20)/100.0);
+ evx = (double)(getlesl(buf, 28)/100.0);
+ evy = (double)(getlesl(buf, 32)/100.0);
+ evz = (double)(getlesl(buf, 36)/100.0);
+ ecef_to_wgs84fix(&session->gpsdata, epx, epy, epz, evx, evy, evz);
+ mask |= LATLON_SET | ALTITUDE_SET | SPEED_SET | TRACK_SET | CLIMB_SET;
+ session->gpsdata.fix.eph = (double)(getlesl(buf, 24)/100.0);
+ session->gpsdata.fix.eps = (double)(getlesl(buf, 40)/100.0);
+ session->gpsdata.pdop = (double)(getleuw(buf, 44)/100.0);
+ session->gpsdata.satellites_used = (int)getub(buf, 47);
+ mask |= PDOP_SET ;
+
+ navmode = getub(buf, 10);
+ switch (navmode){
+ case UBX_MODE_TMONLY:
+ case UBX_MODE_3D:
+ session->gpsdata.fix.mode = MODE_3D;
+ break;
+ case UBX_MODE_2D:
+ case UBX_MODE_DR: /* consider this too as 2D */
+ case UBX_MODE_GPSDR: /* XXX DR-aided GPS may be valid 3D */
+ session->gpsdata.fix.mode = MODE_2D;
+ break;
+ default:
+ session->gpsdata.fix.mode = MODE_NO_FIX;
+ }
+
+ if ((flags & UBX_SOL_FLAG_DGPS) != 0)
+ session->gpsdata.status = STATUS_DGPS_FIX;
+ else if (session->gpsdata.fix.mode != MODE_NO_FIX)
+ session->gpsdata.status = STATUS_FIX;
+
+ mask |= MODE_SET | STATUS_SET | CYCLE_START_SET | USED_SET ;
+
+ return mask;
+}
+
+/**
+ * Dilution of precision message
+ */
+static gps_mask_t
+ubx_msg_nav_dop(struct gps_device_t *session, unsigned char *buf, size_t data_len)
+{
+ if (data_len != 18)
+ return 0;
+
+ session->gpsdata.gdop = (double)(getleuw(buf, 4)/100.0);
+ session->gpsdata.pdop = (double)(getleuw(buf, 6)/100.0);
+ session->gpsdata.tdop = (double)(getleuw(buf, 8)/100.0);
+ session->gpsdata.vdop = (double)(getleuw(buf, 10)/100.0);
+ session->gpsdata.hdop = (double)(getleuw(buf, 12)/100.0);
+
+ return DOP_SET;
+}
+
+/**
+ * GPS Leap Seconds
+ */
+static gps_mask_t
+ubx_msg_nav_timegps(struct gps_device_t *session, unsigned char *buf, size_t data_len)
+{
+ unsigned int gw, tow, flags;
+ double t;
+
+ if (data_len != 16)
+ return 0;
+
+ tow = getleul(buf, 0);
+ gw = (uint)getlesw(buf, 8);
+ if (gw > session->driver.ubx.gps_week)
+ session->driver.ubx.gps_week = gw;
+
+ flags = (unsigned int)getub(buf, 11);
+ if ((flags & 0x7) != 0)
+ session->context->leap_seconds = (int)getub(buf, 10);
+
+ t = gpstime_to_unix((int)session->driver.ubx.gps_week, tow/1000.0) - session->context->leap_seconds;
+ session->gpsdata.sentence_time = session->gpsdata.fix.time = t;
+
+ return TIME_SET | ONLINE_SET;
+}
+
+/**
+ * GPS Satellite Info
+ */
+static gps_mask_t
+ubx_msg_nav_svinfo(struct gps_device_t *session, unsigned char *buf, size_t data_len)
+{
+ unsigned int i, j, tow, nchan, nsv, st;
+
+ if (data_len < 152 ) {
+ gpsd_report(LOG_PROG, "runt svinfo (datalen=%zd)\n", data_len);
+ return 0;
+ }
+ tow = getleul(buf, 0);
+// session->gpsdata.sentence_time = gpstime_to_unix(gps_week, tow)
+// - session->context->leap_seconds;
+ /*@ +charint @*/
+ nchan = getub(buf, 4);
+ if (nchan > MAXCHANNELS){
+ gpsd_report(LOG_WARN, "Invalid NAV SVINFO message, >%d reported",MAXCHANNELS);
+ return 0;
+ }
+ /*@ -charint @*/
+ gpsd_zero_satellites(&session->gpsdata);
+ nsv = 0;
+ for (i = j = st = 0; i < nchan; i++) {
+ unsigned int off = 8 + 12 * i;
+ if((int)getub(buf, off+4) == 0) continue; /* LEA-5H seems to have a bug reporting sats it does not see or hear*/
+ session->gpsdata.PRN[j] = (int)getub(buf, off+1);
+ session->gpsdata.ss[j] = (int)getub(buf, off+4);
+ session->gpsdata.elevation[j] = (int)getsb(buf, off+5);
+ session->gpsdata.azimuth[j] = (int)getlesw(buf, off+6);
+ if(session->gpsdata.PRN[j])
+ st++;
+ /*@ -predboolothers */
+ if (getub(buf, off+2) & 0x01)
+ session->gpsdata.used[nsv++] = session->gpsdata.PRN[j];
+ if (session->gpsdata.PRN[j] == (int)sbas_in_use)
+ session->gpsdata.used[nsv++] = session->gpsdata.PRN[j];
+ /*@ +predboolothers */
+ j++;
+ }
+ session->gpsdata.satellites = (int)st;
+ session->gpsdata.satellites_used = (int)nsv;
+ return SATELLITE_SET | USED_SET;
+}
+
+/*
+ * SBAS Info
+ */
+static void
+ubx_msg_sbas(unsigned char *buf)
+{
+#ifdef UBX_SBAS_DEBUG
+ unsigned int i, nsv;
+
+ gpsd_report(LOG_WARN, "SBAS: %d %d %d %d %d\n",
+ (int)getub(buf, 4), (int)getub(buf, 5), (int)getub(buf, 6), (int)getub(buf, 7), (int)getub(buf, 8));
+
+ nsv = (int)getub(buf, 8);
+ for (i = 0; i < nsv; i++) {
+ int off = 12 + 12 * i;
+ gpsd_report(LOG_WARN, "SBAS info on SV: %d\n", (int)getub(buf, off));
+ }
+#endif
+/* really 'in_use' depends on the sats info, EGNOS is still in test */
+/* In WAAS areas one might also check for the type of corrections indicated */
+ sbas_in_use = getub(buf, 4);
+}
+
+static void
+ubx_msg_inf(unsigned char *buf, size_t data_len)
+{
+ unsigned short msgid;
+ static char txtbuf[MAX_PACKET_LENGTH];
+
+ msgid = (unsigned short)((buf[2] << 8) | buf[3]);
+ if (data_len > MAX_PACKET_LENGTH-1)
+ data_len = MAX_PACKET_LENGTH-1;
+
+ (void)strlcpy(txtbuf, (char *)buf+6, MAX_PACKET_LENGTH); txtbuf[data_len] = '\0';
+ switch (msgid) {
+ case UBX_INF_DEBUG:
+ gpsd_report(LOG_PROG, "UBX_INF_DEBUG: %s\n", txtbuf);
+ break;
+ case UBX_INF_TEST:
+ gpsd_report(LOG_PROG, "UBX_INF_TEST: %s\n", txtbuf);
+ break;
+ case UBX_INF_NOTICE:
+ gpsd_report(LOG_INF, "UBX_INF_NOTICE: %s\n", txtbuf);
+ break;
+ case UBX_INF_WARNING:
+ gpsd_report(LOG_WARN, "UBX_INF_WARNING: %s\n", txtbuf);
+ break;
+ case UBX_INF_ERROR:
+ gpsd_report(LOG_WARN, "UBX_INF_ERROR: %s\n", txtbuf);
+ break;
+ default:
+ break;
+ }
+ return ;
+}
+
+/*@ +charint @*/
+gps_mask_t ubx_parse(struct gps_device_t *session, unsigned char *buf, size_t len)
+{
+ size_t data_len;
+ unsigned short msgid;
+ gps_mask_t mask = 0;
+ int i;
+
+ if (len < 6) /* the packet at least contains a head of six bytes */
+ return 0;
+
+ /* extract message id and length */
+ msgid = (buf[2] << 8) | buf[3];
+ data_len = (size_t)getlesw(buf, 4);
+ switch (msgid)
+ {
+ case UBX_NAV_POSECEF:
+ gpsd_report(LOG_IO, "UBX_NAV_POSECEF\n");
+ break;
+ case UBX_NAV_POSLLH:
+ gpsd_report(LOG_IO, "UBX_NAV_POSLLH\n");
+ break;
+ case UBX_NAV_STATUS:
+ gpsd_report(LOG_IO, "UBX_NAV_STATUS\n");
+ break;
+ case UBX_NAV_DOP:
+ gpsd_report(LOG_PROG, "UBX_NAV_DOP\n");
+ mask = ubx_msg_nav_dop(session, &buf[6], data_len);
+ break;
+ case UBX_NAV_SOL:
+ gpsd_report(LOG_PROG, "UBX_NAV_SOL\n");
+ mask = ubx_msg_nav_sol(session, &buf[6], data_len);
+ break;
+ case UBX_NAV_POSUTM:
+ gpsd_report(LOG_IO, "UBX_NAV_POSUTM\n");
+ break;
+ case UBX_NAV_VELECEF:
+ gpsd_report(LOG_IO, "UBX_NAV_VELECEF\n");
+ break;
+ case UBX_NAV_VELNED:
+ gpsd_report(LOG_IO, "UBX_NAV_VELNED\n");
+ break;
+ case UBX_NAV_TIMEGPS:
+ gpsd_report(LOG_PROG, "UBX_NAV_TIMEGPS\n");
+ mask = ubx_msg_nav_timegps(session, &buf[6], data_len);
+ break;
+ case UBX_NAV_TIMEUTC:
+ gpsd_report(LOG_IO, "UBX_NAV_TIMEUTC\n");
+ break;
+ case UBX_NAV_CLOCK:
+ gpsd_report(LOG_IO, "UBX_NAV_CLOCK\n");
+ break;
+ case UBX_NAV_SVINFO:
+ gpsd_report(LOG_PROG, "UBX_NAV_SVINFO\n");
+ mask = ubx_msg_nav_svinfo(session, &buf[6], data_len);
+ break;
+ case UBX_NAV_DGPS:
+ gpsd_report(LOG_IO, "UBX_NAV_DGPS\n");
+ break;
+ case UBX_NAV_SBAS:
+ gpsd_report(LOG_IO, "UBX_NAV_SBAS\n");
+ ubx_msg_sbas(&buf[6]);
+ break;
+ case UBX_NAV_EKFSTATUS:
+ gpsd_report(LOG_IO, "UBX_NAV_EKFSTATUS\n");
+ break;
+
+ case UBX_RXM_RAW:
+ gpsd_report(LOG_IO, "UBX_RXM_RAW\n");
+ break;
+ case UBX_RXM_SFRB:
+ gpsd_report(LOG_IO, "UBX_RXM_SFRB\n");
+ break;
+ case UBX_RXM_SVSI:
+ gpsd_report(LOG_PROG, "UBX_RXM_SVSI\n");
+ break;
+ case UBX_RXM_ALM:
+ gpsd_report(LOG_IO, "UBX_RXM_ALM\n");
+ break;
+ case UBX_RXM_EPH:
+ gpsd_report(LOG_IO, "UBX_RXM_EPH\n");
+ break;
+ case UBX_RXM_POSREQ:
+ gpsd_report(LOG_IO, "UBX_RXM_POSREQ\n");
+ break;
+
+ case UBX_MON_SCHED:
+ gpsd_report(LOG_IO, "UBX_MON_SCHED\n");
+ break;
+ case UBX_MON_IO:
+ gpsd_report(LOG_IO, "UBX_MON_IO\n");
+ break;
+ case UBX_MON_IPC:
+ gpsd_report(LOG_IO, "UBX_MON_IPC\n");
+ break;
+ case UBX_MON_VER:
+ gpsd_report(LOG_IO, "UBX_MON_VER\n");
+ break;
+ case UBX_MON_EXCEPT:
+ gpsd_report(LOG_IO, "UBX_MON_EXCEPT\n");
+ break;
+ case UBX_MON_MSGPP:
+ gpsd_report(LOG_IO, "UBX_MON_MSGPP\n");
+ break;
+ case UBX_MON_RXBUF:
+ gpsd_report(LOG_IO, "UBX_MON_RXBUF\n");
+ break;
+ case UBX_MON_TXBUF:
+ gpsd_report(LOG_IO, "UBX_MON_TXBUF\n");
+ break;
+ case UBX_MON_HW:
+ gpsd_report(LOG_IO, "UBX_MON_HW\n");
+ break;
+ case UBX_MON_USB:
+ gpsd_report(LOG_IO, "UBX_MON_USB\n");
+ break;
+
+ case UBX_INF_DEBUG:
+ /* FALLTHROUGH */
+ case UBX_INF_TEST:
+ /* FALLTHROUGH */
+ case UBX_INF_NOTICE:
+ /* FALLTHROUGH */
+ case UBX_INF_WARNING:
+ /* FALLTHROUGH */
+ case UBX_INF_ERROR:
+ ubx_msg_inf(buf, data_len);
+ break;
+
+ case UBX_TIM_TP:
+ gpsd_report(LOG_IO, "UBX_TIM_TP\n");
+ break;
+ case UBX_TIM_TM:
+ gpsd_report(LOG_IO, "UBX_TIM_TM\n");
+ break;
+ case UBX_TIM_TM2:
+ gpsd_report(LOG_IO, "UBX_TIM_TM2\n");
+ break;
+ case UBX_TIM_SVIN:
+ gpsd_report(LOG_IO, "UBX_TIM_SVIN\n");
+ break;
+
+ case UBX_CFG_PRT:
+ gpsd_report(LOG_IO, "UBX_CFG_PRT\n");
+ for(i=6;i<26;i++)
+ original_port_settings[i-6] = buf[i]; /* copy the original port settings */
+ buf[14+6] &= ~0x02; /* turn off NMEA output on this port */
+ (void)ubx_write(session, 0x06, 0x00, &buf[6], 20); /* send back with all other settings intact */
+ have_port_configuration = true;
+ break;
+
+ case UBX_ACK_NAK:
+ gpsd_report(LOG_IO, "UBX_ACK_NAK, class: %02x, id: %02x\n", buf[6], buf[7]);
+ break;
+ case UBX_ACK_ACK:
+ gpsd_report(LOG_IO, "UBX_ACK_ACK, class: %02x, id: %02x\n", buf[6], buf[7]);
+ break;
+
+ default:
+ gpsd_report(LOG_WARN,
+ "UBX: unknown packet id 0x%04hx (length %zd) %s\n",
+ msgid, len, gpsd_hexdump_wrapper(buf, len, LOG_WARN));
+ }
+
+ if (mask)
+ (void)snprintf(session->gpsdata.tag, sizeof(session->gpsdata.tag),
+ "0x%04hx", msgid);
+
+ return mask | ONLINE_SET;
+}
+/*@ -charint @*/
+
+static gps_mask_t parse_input(struct gps_device_t *session)
+{
+ gps_mask_t st;
+
+ if (session->packet.type == UBX_PACKET){
+ st = ubx_parse(session, session->packet.outbuffer, session->packet.outbuflen);
+ 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);
+ session->gpsdata.driver_mode = MODE_NMEA;
+ return st;
+#endif /* NMEA_ENABLE */
+ } else
+ return 0;
+}
+
+void ubx_catch_model(struct gps_device_t *session, unsigned char *buf, size_t len)
+{
+ /*@ +charint */
+ unsigned char *ip = &buf[19];
+ unsigned char *op = (unsigned char *)session->subtype;
+ size_t end = ((len - 19) < 63)?(len - 19):63;
+ size_t i;
+
+ for(i=0;i<end;i++) {
+ if((*ip == 0x00) || (*ip == '*')) {
+ *op = 0x00;
+ break;
+ }
+ *(op++) = *(ip++);
+ }
+ /*@ -charint */
+}
+
+bool ubx_write(struct gps_device_t *session,
+ unsigned int msg_class, unsigned int msg_id,
+ unsigned char *msg, unsigned short data_len)
+{
+ unsigned char CK_A, CK_B;
+ ssize_t i, count;
+ bool ok;
+
+ /*@ -type @*/
+ session->msgbuf[0] = 0xb5;
+ session->msgbuf[1] = 0x62;
+
+ CK_A = CK_B = 0;
+ session->msgbuf[2] = msg_class;
+ session->msgbuf[3] = msg_id;
+ session->msgbuf[4] = data_len & 0xff;
+ session->msgbuf[5] = (data_len >> 8) & 0xff;
+
+ assert(msg != NULL || data_len == 0);
+ if (msg != NULL)
+ (void)memcpy(&session->msgbuf[6], msg, data_len);
+
+ /* calculate CRC */
+ for (i = 2; i < 6; i++) {
+ CK_A += session->msgbuf[i];
+ CK_B += CK_A;
+ }
+ /*@ -nullderef @*/
+ for (i = 0; i < data_len; i++) {
+ CK_A += msg[i];
+ CK_B += CK_A;
+ }
+
+ session->msgbuf[6 + data_len] = CK_A;
+ session->msgbuf[7 + data_len] = CK_B;
+ session->msgbuflen = data_len + 7;
+ /*@ +type @*/
+
+ gpsd_report(LOG_IO,
+ "=> GPS: UBX class: %02x, id: %02x, len: %d, data:%s, crc: %02x%02x\n",
+ msg_class, msg_id, data_len,
+ gpsd_hexdump_wrapper(msg, (size_t)data_len, LOG_IO),
+ CK_A, CK_B);
+
+ count = write(session->gpsdata.gps_fd,
+ session->msgbuf, session->msgbuflen);
+ (void)tcdrain(session->gpsdata.gps_fd);
+ ok = (count == (ssize_t)session->msgbuflen);
+ /*@ +nullderef @*/
+ return(ok);
+}
+
+static ssize_t ubx_control_send(struct gps_device_t *session, char *msg, size_t data_len)
+/* not used by gpsd, it's for gpsctl and friends */
+{
+ return ubx_write(session, (unsigned int)msg[0], (unsigned int)msg[1],
+ (unsigned char *)msg+2, (unsigned short)(data_len-2)) ? ((ssize_t)(data_len+7)) : -1;
+}
+
+#ifdef ALLOW_RECONFIGURE
+static void ubx_configure(struct gps_device_t *session, unsigned int seq)
+{
+ unsigned char msg[32];
+
+ gpsd_report(LOG_IO, "UBX configure: %d\n",seq);
+
+ (void)ubx_write(session, 0x06u, 0x00, NULL, 0); /* get this port's settings */
+
+ /*@ -type @*/
+ msg[0] = 0x03; /* SBAS mode enabled, accept testbed mode */
+ msg[1] = 0x07; /* SBAS usage: range, differential corrections and integrity */
+ msg[2] = 0x03; /* use the maximun search range: 3 channels */
+ msg[3] = 0x00; /* PRN numbers to search for all set to 0 => auto scan */
+ msg[4] = 0x00;
+ msg[5] = 0x00;
+ msg[6] = 0x00;
+ msg[7] = 0x00;
+ (void)ubx_write(session, 0x06u, 0x16, msg, 8);
+
+ msg[0] = 0x01; /* class */
+ msg[1] = 0x04; /* msg id = UBX_NAV_DOP */
+ msg[2] = 0x01; /* rate */
+ (void)ubx_write(session, 0x06u, 0x01, msg, 3);
+ msg[0] = 0x01; /* class */
+ msg[1] = 0x06; /* msg id = NAV-SOL */
+ msg[2] = 0x01; /* rate */
+ (void)ubx_write(session, 0x06u, 0x01, msg, 3);
+ msg[0] = 0x01; /* class */
+ msg[1] = 0x20; /* msg id = UBX_NAV_TIMEGPS */
+ msg[2] = 0x01; /* rate */
+ (void)ubx_write(session, 0x06u, 0x01, msg, 3);
+ msg[0] = 0x01; /* class */
+ msg[1] = 0x30; /* msg id = NAV-SVINFO */
+ msg[2] = 0x0a; /* rate */
+ (void)ubx_write(session, 0x06u, 0x01, msg, 3);
+ msg[0] = 0x01; /* class */
+ msg[1] = 0x32; /* msg id = NAV-SBAS */
+ msg[2] = 0x0a; /* rate */
+ (void)ubx_write(session, 0x06u, 0x01, msg, 3);
+ /*@ +type @*/
+
+}
+
+static void ubx_revert(struct gps_device_t *session)
+{
+ /*@ -type @*/
+ unsigned char msg[4] = {
+ 0x00, 0x00, /* hotstart */
+ 0x01, /* controlled software reset */
+ 0x00}; /* reserved */
+ /*@ +type @*/
+
+ gpsd_report(LOG_IO, "UBX revert\n");
+
+ /* Reverting all in one fast and reliable reset */
+ (void)ubx_write(session, 0x06, 0x04, msg, 4); /* CFG-RST */
+}
+#endif /* ALLOW_RECONFIGURE */
+
+static void ubx_nmea_mode(struct gps_device_t *session, int mode)
+{
+ int i;
+ unsigned char buf[20];
+
+ if(!have_port_configuration)
+ return;
+
+ /*@ +charint -usedef @*/
+ for(i=0;i<22;i++)
+ buf[i] = original_port_settings[i]; /* copy the original port settings */
+ if(buf[0] == 0x01) /* set baudrate on serial port only */
+ putlelong(buf, 8, session->gpsdata.baudrate);
+
+ if (mode == 0) {
+ buf[14] &= ~0x01; /* turn off UBX output on this port */
+ buf[14] |= 0x02; /* turn on NMEA output on this port */
+ } else {
+ buf[14] &= ~0x02; /* turn off NMEA output on this port */
+ buf[14] |= 0x01; /* turn on UBX output on this port */
+ }
+ /*@ -charint +usedef @*/
+ (void)ubx_write(session, 0x06u, 0x00, &buf[6], 20); /* send back with all other settings intact */
+}
+
+static bool ubx_speed(struct gps_device_t *session,
+ speed_t speed, char parity, int stopbits)
+{
+ int i;
+ unsigned char buf[20];
+ unsigned long usart_mode;
+
+ /*@ +charint -usedef -compdef */
+ if((!have_port_configuration) || (buf[0] != 0x01)) /* set baudrate on serial port only */
+ return false;
+
+ for(i=0;i<22;i++)
+ buf[i] = original_port_settings[i]; /* copy the original port settings */
+ usart_mode = getleul(buf, 4);
+ usart_mode &=~ 0xE00; /* zero bits 11:9 */
+ switch (parity) {
+ case (int)'E':
+ case 2:
+ usart_mode |= 0x00;
+ break;
+ case (int)'O':
+ case 1:
+ usart_mode |= 0x01;
+ break;
+ case (int)'N':
+ case 0:
+ default:
+ usart_mode |= 0x4; /* 0x5 would work too */
+ break;
+ }
+ usart_mode &=~ 0x03000; /* zero bits 13:12 */
+ if (stopbits == 2)
+ usart_mode |= 0x2000; /* zero value means 1 stop bit */
+ putlelong(buf, 4, usart_mode);
+ putlelong(buf, 8, speed);
+ (void)ubx_write(session, 0x06, 0x00, &buf[6], 20); /* send back with all other settings intact */
+ /*@ -charint +usedef +compdef */
+ return true;
+}
+
+/* This is everything we export */
+const struct gps_type_t ubx_binary = {
+ .type_name = "uBlox UBX binary", /* Full name of type */
+ .packet_type = UBX_PACKET, /* associated lexer packet type */
+ .trigger = NULL, /* Response string that identifies device (not active) */
+ .channels = 50, /* Number of satellite channels supported by the device */
+ .control_send = ubx_control_send, /* no control sender yet */
+ .probe_detect = NULL, /* Startup-time device detector */
+ .probe_wakeup = NULL, /* Wakeup to be done before each baud hunt */
+ .probe_subtype = NULL, /* Initialize the device and get subtype */
+#ifdef ALLOW_RECONFIGURE
+ .configurator = ubx_configure, /* Enable what reports we need */
+#endif /* ALLOW_RECONFIGURE */
+ .get_packet = generic_get, /* Packet getter (using default routine) */
+ .parse_packet = parse_input, /* Parse message packets */
+ .rtcm_writer = NULL, /* RTCM handler (using default routine) */
+ .speed_switcher = ubx_speed, /* Speed (baudrate) switch */
+ .mode_switcher = ubx_nmea_mode, /* Switch to NMEA mode */
+ .rate_switcher = NULL, /* Message delivery rate switcher */
+ .cycle_chars = -1, /* Number of chars per report cycle */
+#ifdef ALLOW_RECONFIGURE
+ .revert = ubx_revert, /* Undo the actions of .configurator */
+#endif /* ALLOW_RECONFIGURE */
+ .wrapup = NULL, /* Puts device back to original settings */
+ .cycle = 1 /* Number of updates per second */
+};
+#endif /* defined(UBX_ENABLE) && defined(BINARY_ENABLE) */