driver_zodiac.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. /*
  2. * Handle the Rockwell binary packet format supported by the old Zodiac chipset
  3. *
  4. * Week counters are not limited to 10 bits. It's unknown what
  5. * the firmware is doing to disambiguate them, if anything; it might just
  6. * be adding a fixed offset based on a hidden epoch value, in which case
  7. * unhappy things will occur on the next rollover.
  8. *
  9. * This file is Copyright 2010 by the GPSD project
  10. * SPDX-License-Identifier: BSD-2-clause
  11. */
  12. #include "gpsd_config.h" /* must be before all includes */
  13. #include <math.h>
  14. #include <stdbool.h>
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <unistd.h>
  18. #include "gpsd.h"
  19. #include "bits.h"
  20. #include "strfuncs.h"
  21. /* Zodiac protocol description uses 1-origin indexing by little-endian word */
  22. #define get16z(buf, n) ((buf[2*(n)-2]) | (buf[2*(n)-1] << 8))
  23. #define getu16z(buf, n) (uint16_t)((buf[2*(n)-2]) | (buf[2*(n)-1] << 8))
  24. #define get32z(buf, n) ((buf[2*(n)-2]) | (buf[2*(n)-1] << 8) | \
  25. (buf[2*(n)+0] << 16) | (buf[2*(n)+1] << 24))
  26. #define getu32z(buf, n) (uint32_t)((buf[2*(n)-2]) | (buf[2*(n)-1] << 8) | \
  27. (buf[2*(n)+0] << 16) | (buf[2*(n)+1] << 24))
  28. #define getstringz(to, from, s, e) \
  29. (void)memcpy(to, from+2*(s)-2, 2*((e)-(s)+1))
  30. #ifdef ZODIAC_ENABLE
  31. struct header
  32. {
  33. unsigned short sync;
  34. unsigned short id;
  35. unsigned short ndata;
  36. unsigned short flags;
  37. unsigned short csum;
  38. };
  39. static unsigned short zodiac_checksum(unsigned short *w, int n)
  40. {
  41. unsigned short csum = 0;
  42. while (n-- > 0)
  43. csum += *(w++);
  44. return -csum;
  45. }
  46. static ssize_t end_write(int fd, void *d, size_t len)
  47. /* write an array of shorts in little-endian format */
  48. {
  49. unsigned char buf[BUFSIZ];
  50. short *data = (short *)d;
  51. size_t n;
  52. for (n = 0; n < (size_t)(len/2); n++)
  53. putle16(buf, n*2, data[n]);
  54. return write(fd, (char*)buf, len);
  55. }
  56. /* zodiac_spew - Takes a message type, an array of data words, and a length
  57. * for the array, and prepends a 5 word header (including checksum).
  58. * The data words are expected to be checksummed.
  59. */
  60. static ssize_t zodiac_spew(struct gps_device_t *session, unsigned short type,
  61. unsigned short *dat, int dlen)
  62. {
  63. struct header h;
  64. int i;
  65. char buf[BUFSIZ];
  66. h.sync = 0x81ff;
  67. h.id = (unsigned short)type;
  68. h.ndata = (unsigned short)(dlen - 1);
  69. h.flags = 0;
  70. h.csum = zodiac_checksum((unsigned short *)&h, 4);
  71. if (!BAD_SOCKET(session->gpsdata.gps_fd)) {
  72. size_t hlen, datlen;
  73. hlen = sizeof(h);
  74. datlen = sizeof(unsigned short) * dlen;
  75. if (end_write(session->gpsdata.gps_fd, &h, hlen) != (ssize_t) hlen ||
  76. end_write(session->gpsdata.gps_fd, dat,
  77. datlen) != (ssize_t) datlen) {
  78. GPSD_LOG(LOG_INFO, &session->context->errout,
  79. "Reconfigure write failed\n");
  80. return -1;
  81. }
  82. }
  83. (void)snprintf(buf, sizeof(buf),
  84. "%04x %04x %04x %04x %04x",
  85. h.sync, h.id, h.ndata, h.flags, h.csum);
  86. for (i = 0; i < dlen; i++)
  87. str_appendf(buf, sizeof(buf), " %04x", dat[i]);
  88. GPSD_LOG(LOG_RAW, &session->context->errout,
  89. "Sent Zodiac packet: %s\n", buf);
  90. return 0;
  91. }
  92. static void send_rtcm(struct gps_device_t *session,
  93. const char *rtcmbuf, size_t rtcmbytes)
  94. {
  95. unsigned short data[34];
  96. int n = 1 + (int)(rtcmbytes / 2 + rtcmbytes % 2);
  97. if (session->driver.zodiac.sn++ > 32767)
  98. session->driver.zodiac.sn = 0;
  99. memset(data, 0, sizeof(data));
  100. data[0] = session->driver.zodiac.sn; /* sequence number */
  101. memcpy(&data[1], rtcmbuf, rtcmbytes);
  102. data[n] = zodiac_checksum(data, n);
  103. (void)zodiac_spew(session, 1351, data, n + 1);
  104. }
  105. static ssize_t zodiac_send_rtcm(struct gps_device_t *session,
  106. const char *rtcmbuf, size_t rtcmbytes)
  107. {
  108. while (rtcmbytes > 0) {
  109. size_t len = (size_t) (rtcmbytes > 64 ? 64 : rtcmbytes);
  110. send_rtcm(session, rtcmbuf, len);
  111. rtcmbytes -= len;
  112. rtcmbuf += len;
  113. }
  114. return 1;
  115. }
  116. #define getzword(n) get16z(session->lexer.outbuffer, n)
  117. #define getzu16(n) getu16z(session->lexer.outbuffer, n)
  118. #define getzlong(n) get32z(session->lexer.outbuffer, n)
  119. #define getzu32(n) getu32z(session->lexer.outbuffer, n)
  120. static gps_mask_t handle1000(struct gps_device_t *session)
  121. /* time-position-velocity report */
  122. {
  123. gps_mask_t mask;
  124. struct tm unpacked_date;
  125. int datum;
  126. char ts_buf[TIMESPEC_LEN];
  127. /* ticks = getzlong(6); */
  128. /* sequence = getzword(8); */
  129. /* measurement_sequence = getzword(9); */
  130. session->newdata.status = (getzword(10) & 0x1c) ? 0 : 1;
  131. if (session->newdata.status != 0)
  132. session->newdata.mode = (getzword(10) & 1) ? MODE_2D : MODE_3D;
  133. else
  134. session->newdata.mode = MODE_NO_FIX;
  135. /* solution_type = getzword(11); */
  136. session->gpsdata.satellites_used = (int)getzword(12);
  137. /* polar_navigation = getzword(13); */
  138. session->context->gps_week = (unsigned short)getzword(14);
  139. /* gps_seconds = getzlong(15); */
  140. /* gps_nanoseconds = getzlong(17); */
  141. unpacked_date.tm_mday = (int)getzword(19);
  142. unpacked_date.tm_mon = (int)getzword(20) - 1;
  143. unpacked_date.tm_year = (int)getzword(21) - 1900;
  144. unpacked_date.tm_hour = (int)getzword(22);
  145. unpacked_date.tm_min = (int)getzword(23);
  146. unpacked_date.tm_sec = (int)getzword(24);
  147. unpacked_date.tm_isdst = 0;
  148. session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
  149. session->newdata.time.tv_nsec = getzu32(25);
  150. session->newdata.latitude = ((long)getzlong(27)) * RAD_2_DEG * 1e-8;
  151. session->newdata.longitude = ((long)getzlong(29)) * RAD_2_DEG * 1e-8;
  152. /*
  153. * The Rockwell Jupiter TU30-D140 reports altitude as uncorrected height
  154. * above WGS84 geoid. The Zodiac binary protocol manual does not
  155. * specify whether word 31 is geodetic or WGS 84.
  156. * Here we assume altitude is always wgs84.
  157. */
  158. session->newdata.altHAE = ((long)getzlong(31)) * 1e-2;
  159. session->newdata.geoid_sep = ((short)getzword(33)) * 1e-2;
  160. session->newdata.speed = (int)getzlong(34) * 1e-2;
  161. session->newdata.track = (int)getzword(36) * RAD_2_DEG * 1e-3;
  162. session->newdata.magnetic_var = ((short)getzword(37)) * RAD_2_DEG * 1e-4;
  163. session->newdata.climb = ((short)getzword(38)) * 1e-2;
  164. datum = getzword(39);
  165. datum_code_string(datum, session->newdata.datum,
  166. sizeof(session->newdata.datum));
  167. /*
  168. * The manual says these are 1-sigma. Device reports only eph, circular
  169. * error. Let gpsd_model_error() do the rest
  170. */
  171. session->newdata.eph = (int)getzlong(40) * 1e-2 * GPSD_CONFIDENCE;
  172. session->newdata.epv = (int)getzlong(42) * 1e-2 * GPSD_CONFIDENCE;
  173. session->newdata.ept = (int)getzlong(44) * 1e-2 * GPSD_CONFIDENCE;
  174. session->newdata.eps = (int)getzword(46) * 1e-2 * GPSD_CONFIDENCE;
  175. /* clock_bias = (int)getzlong(47) * 1e-2; */
  176. /* clock_bias_sd = (int)getzlong(49) * 1e-2; */
  177. /* clock_drift = (int)getzlong(51) * 1e-2; */
  178. /* clock_drift_sd = (int)getzlong(53) * 1e-2; */
  179. mask = TIME_SET | NTPTIME_IS | LATLON_SET | ALTITUDE_SET | CLIMB_SET |
  180. SPEED_SET | TRACK_SET | STATUS_SET | MODE_SET |
  181. HERR_SET | SPEEDERR_SET | VERR_SET;
  182. GPSD_LOG(LOG_DATA, &session->context->errout,
  183. "1000: time=%s lat=%.2f lon=%.2f altHAE=%.2f track=%.2f "
  184. "speed=%.2f climb=%.2f mode=%d status=%d\n",
  185. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
  186. session->newdata.latitude,
  187. session->newdata.longitude, session->newdata.altHAE,
  188. session->newdata.track, session->newdata.speed,
  189. session->newdata.climb, session->newdata.mode,
  190. session->newdata.status);
  191. return mask;
  192. }
  193. /* Message 1002: Channel Summary Message */
  194. static gps_mask_t handle1002(struct gps_device_t *session)
  195. {
  196. int i;
  197. timespec_t ts_tow;
  198. /* ticks = getzlong(6); */
  199. /* sequence = getzword(8); */
  200. /* measurement_sequence = getzword(9); */
  201. unsigned short gps_week = getzu16(10);
  202. time_t gps_seconds = (time_t)getzu32(11);
  203. unsigned long gps_nanoseconds = getzu32(13);
  204. char ts_buf[TIMESPEC_LEN];
  205. /* Note: this week counter is not limited to 10 bits. */
  206. session->context->gps_week = gps_week;
  207. session->gpsdata.satellites_used = 0;
  208. for (i = 0; i < ZODIAC_CHANNELS; i++) {
  209. int status, prn;
  210. session->driver.zodiac.Zv[i] = status = (int)getzword(15 + (3 * i));
  211. session->driver.zodiac.Zs[i] = prn = (int)getzword(16 + (3 * i));
  212. if (status & 1)
  213. session->gpsdata.satellites_used++;
  214. session->gpsdata.skyview[i].PRN = (short)prn;
  215. session->gpsdata.skyview[i].ss = (float)getzword(17 + (3 * i));
  216. session->gpsdata.skyview[i].used = (bool)(status & 1);
  217. }
  218. ts_tow.tv_sec = gps_seconds;
  219. ts_tow.tv_nsec = gps_nanoseconds;
  220. session->gpsdata.skyview_time = gpsd_gpstime_resolv(session, gps_week,
  221. ts_tow);
  222. GPSD_LOG(LOG_DATA, &session->context->errout,
  223. "1002: visible=%d used=%d mask={SATELLITE|USED} time %s\n",
  224. session->gpsdata.satellites_visible,
  225. session->gpsdata.satellites_used,
  226. timespec_str(&session->gpsdata.skyview_time, ts_buf,
  227. sizeof(ts_buf)));
  228. return SATELLITE_SET | USED_IS;
  229. }
  230. static gps_mask_t handle1003(struct gps_device_t *session)
  231. /* skyview report */
  232. {
  233. int i, n;
  234. /* The Polaris (and probably the DAGR) emit some strange variant of
  235. * this message which causes gpsd to crash filtering on impossible
  236. * number of satellites avoids this */
  237. n = (int)getzword(14);
  238. if ((n < 0) || (n > 12))
  239. return 0;
  240. gpsd_zero_satellites(&session->gpsdata);
  241. /* ticks = getzlong(6); */
  242. /* sequence = getzword(8); */
  243. session->gpsdata.dop.gdop = (unsigned int)getzword(9) * 1e-2;
  244. session->gpsdata.dop.pdop = (unsigned int)getzword(10) * 1e-2;
  245. session->gpsdata.dop.hdop = (unsigned int)getzword(11) * 1e-2;
  246. session->gpsdata.dop.vdop = (unsigned int)getzword(12) * 1e-2;
  247. session->gpsdata.dop.tdop = (unsigned int)getzword(13) * 1e-2;
  248. session->gpsdata.satellites_visible = n;
  249. for (i = 0; i < ZODIAC_CHANNELS; i++) {
  250. if (i < session->gpsdata.satellites_visible) {
  251. session->gpsdata.skyview[i].PRN = (short)getzword(15 + (3 * i));
  252. session->gpsdata.skyview[i].azimuth =
  253. (((double)getzword(16 + (3 * i))) * RAD_2_DEG * 1e-4);
  254. if (session->gpsdata.skyview[i].azimuth < 0)
  255. session->gpsdata.skyview[i].azimuth += 360;
  256. session->gpsdata.skyview[i].elevation =
  257. (((double)getzword(17 + (3 * i))) * RAD_2_DEG * 1e-4);
  258. } else {
  259. session->gpsdata.skyview[i].PRN = 0;
  260. session->gpsdata.skyview[i].azimuth = NAN;
  261. session->gpsdata.skyview[i].elevation = NAN;
  262. session->gpsdata.skyview[i].ss = NAN;
  263. }
  264. }
  265. session->gpsdata.skyview_time.tv_sec = 0;
  266. session->gpsdata.skyview_time.tv_nsec = 0;
  267. GPSD_LOG(LOG_DATA, &session->context->errout,
  268. "NAVDOP: visible=%d gdop=%.2f pdop=%.2f "
  269. "hdop=%.2f vdop=%.2f tdop=%.2f mask={SATELLITE|DOP}\n",
  270. session->gpsdata.satellites_visible,
  271. session->gpsdata.dop.gdop,
  272. session->gpsdata.dop.hdop,
  273. session->gpsdata.dop.vdop,
  274. session->gpsdata.dop.pdop, session->gpsdata.dop.tdop);
  275. return SATELLITE_SET | DOP_SET;
  276. }
  277. static void handle1005(struct gps_device_t *session UNUSED)
  278. /* fix quality report */
  279. {
  280. /* ticks = getzlong(6); */
  281. /* sequence = getzword(8); */
  282. int numcorrections = (int)getzword(12);
  283. if (session->newdata.mode == MODE_NO_FIX)
  284. session->newdata.status = STATUS_NO_FIX;
  285. else if (numcorrections == 0)
  286. session->newdata.status = STATUS_FIX;
  287. else
  288. session->newdata.status = STATUS_DGPS_FIX;
  289. }
  290. static gps_mask_t handle1011(struct gps_device_t *session)
  291. /* version report */
  292. {
  293. /*
  294. * This is UNTESTED -- but harmless if buggy. Added to support
  295. * client querying of the ID with firmware version in 2006.
  296. * The Zodiac is supposed to send one of these messages on startup.
  297. */
  298. /* software version field */
  299. getstringz(session->subtype, session->lexer.outbuffer, 19, 28);
  300. GPSD_LOG(LOG_DATA, &session->context->errout,
  301. "1011: subtype=%s mask={DEVICEID}\n",
  302. session->subtype);
  303. return DEVICEID_SET;
  304. }
  305. static void handle1108(struct gps_device_t *session)
  306. /* leap-second correction report */
  307. {
  308. /* ticks = getzlong(6); */
  309. /* sequence = getzword(8); */
  310. /* utc_week_seconds = getzlong(14); */
  311. /* leap_nanoseconds = getzlong(17); */
  312. if ((int)(getzword(19) & 3) == 3) {
  313. session->context->valid |= LEAP_SECOND_VALID;
  314. session->context->leap_seconds = (int)getzword(16);
  315. }
  316. }
  317. static gps_mask_t zodiac_analyze(struct gps_device_t *session)
  318. {
  319. unsigned int id =
  320. (unsigned int)((session->lexer.outbuffer[3] << 8) |
  321. session->lexer.outbuffer[2]);
  322. /*
  323. * The guard looks superfluous, but it keeps the rather expensive
  324. * gpsd_packetdump() function from being called even when the debug
  325. * level does not actually require it.
  326. */
  327. if (session->context->errout.debug >= LOG_RAW)
  328. GPSD_LOG(LOG_RAW, &session->context->errout,
  329. "Raw Zodiac packet type %d length %zd: %s\n",
  330. id, session->lexer.outbuflen, gpsd_prettydump(session));
  331. if (session->lexer.outbuflen < 10)
  332. return 0;
  333. /*
  334. * Normal cycle for these devices is 1001 1002.
  335. * We count 1001 as end of cycle because 1002 doesn't
  336. * carry fix information.
  337. */
  338. session->cycle_end_reliable = true;
  339. switch (id) {
  340. case 1000:
  341. return handle1000(session) | (CLEAR_IS | REPORT_IS);
  342. case 1002:
  343. return handle1002(session);
  344. case 1003:
  345. return handle1003(session);
  346. case 1005:
  347. handle1005(session);
  348. return 0;
  349. case 1011:
  350. return handle1011(session);
  351. case 1108:
  352. handle1108(session);
  353. return 0;
  354. default:
  355. return 0;
  356. }
  357. }
  358. static ssize_t zodiac_control_send(struct gps_device_t *session,
  359. char *msg, size_t len)
  360. {
  361. unsigned short shortwords[256];
  362. #define min(x,y) ((x) < (y) ? x : y)
  363. /*
  364. * We used to just cast msg to an unsigned short pointer.
  365. * This can fail on word-oriented architectures like a SPARC.
  366. */
  367. memcpy((char *)shortwords, msg, min(sizeof(shortwords), len));
  368. /* and if len isn't even, it's your own fault */
  369. return zodiac_spew(session, shortwords[0], shortwords + 1,
  370. (int)(len / 2 - 1));
  371. }
  372. static bool zodiac_speed_switch(struct gps_device_t *session,
  373. speed_t speed, char parity, int stopbits)
  374. {
  375. unsigned short data[15];
  376. if (session->driver.zodiac.sn++ > 32767)
  377. session->driver.zodiac.sn = 0;
  378. switch (parity) {
  379. case 'E':
  380. case 2:
  381. parity = (char)2;
  382. break;
  383. case 'O':
  384. case 1:
  385. parity = (char)1;
  386. break;
  387. case 'N':
  388. case 0:
  389. default:
  390. parity = (char)0;
  391. break;
  392. }
  393. memset(data, 0, sizeof(data));
  394. /* data is the part of the message starting at word 6 */
  395. data[0] = session->driver.zodiac.sn; /* sequence number */
  396. data[1] = 1; /* port 1 data valid */
  397. data[2] = (unsigned short)parity; /* port 1 character width (8 bits) */
  398. /* port 1 stop bits (1 stopbit) */
  399. data[3] = (unsigned short)(stopbits - 1);
  400. data[4] = 0; /* port 1 parity (none) */
  401. /* port 1 speed */
  402. data[5] = (unsigned short)(round(log((double)speed / 300) / GPS_LN2) + 1);
  403. data[14] = zodiac_checksum(data, 14);
  404. (void)zodiac_spew(session, 1330, data, 15);
  405. return true; /* it would be nice to error-check this */
  406. }
  407. static double zodiac_time_offset(struct gps_device_t *session UNUSED)
  408. {
  409. /* Removing/changing the magic number below is likely to disturb
  410. * the handling of the 1pps signal from the gps device. The regression
  411. * tests and simple gps applications do not detect this. A live test
  412. * with the 1pps signal active is required. */
  413. return 1.1;
  414. }
  415. /* this is everything we export */
  416. /* *INDENT-OFF* */
  417. const struct gps_type_t driver_zodiac =
  418. {
  419. .type_name = "Zodiac", /* full name of type */
  420. .packet_type = ZODIAC_PACKET, /* associated lexer packet type */
  421. .flags = DRIVER_STICKY, /* no flags set */
  422. .trigger = NULL, /* no trigger */
  423. .channels = 12, /* consumer-grade GPS */
  424. .probe_detect = NULL, /* no probe */
  425. .get_packet = generic_get, /* use the generic packet getter */
  426. .parse_packet = zodiac_analyze, /* parse message packets */
  427. .rtcm_writer = zodiac_send_rtcm, /* send DGPS correction */
  428. .init_query = NULL, /* non-perturbing initial query */
  429. .event_hook = NULL, /* no configuration */
  430. .speed_switcher = zodiac_speed_switch,/* we can change baud rate */
  431. .mode_switcher = NULL, /* no mode switcher */
  432. .rate_switcher = NULL, /* no sample-rate switcher */
  433. .min_cycle.tv_sec = 1, /* not relevant, no rate switch */
  434. .min_cycle.tv_nsec = 0, /* not relevant, no rate switch */
  435. .control_send = zodiac_control_send, /* for gpsctl and friends */
  436. .time_offset = zodiac_time_offset, /* compute NTO fudge factor */
  437. };
  438. /* *INDENT-ON* */
  439. #endif /* ZODIAC_ENABLE */
  440. // vim: set expandtab shiftwidth=4