driver_italk.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. /*
  2. * Driver for the iTalk binary protocol used by FasTrax
  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. */
  13. #include "gpsd_config.h" /* must be before all includes */
  14. #include <math.h>
  15. #include <stdbool.h>
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include <unistd.h>
  19. #include "gpsd.h"
  20. #if defined(ITRAX_ENABLE) && defined(BINARY_ENABLE)
  21. #include "bits.h"
  22. #include "driver_italk.h"
  23. #include "timespec.h"
  24. static gps_mask_t italk_parse(struct gps_device_t *, unsigned char *, size_t);
  25. static gps_mask_t decode_itk_navfix(struct gps_device_t *, unsigned char *,
  26. size_t);
  27. static gps_mask_t decode_itk_prnstatus(struct gps_device_t *, unsigned char *,
  28. size_t);
  29. static gps_mask_t decode_itk_utcionomodel(struct gps_device_t *,
  30. unsigned char *, size_t);
  31. static gps_mask_t decode_itk_subframe(struct gps_device_t *, unsigned char *,
  32. size_t);
  33. /* NAVIGATION_MSG, message id 7 */
  34. static gps_mask_t decode_itk_navfix(struct gps_device_t *session,
  35. unsigned char *buf, size_t len)
  36. {
  37. unsigned short flags, pflags;
  38. timespec_t ts_tow;
  39. uint32_t tow; /* Time of week [ms] */
  40. char ts_buf[TIMESPEC_LEN];
  41. gps_mask_t mask = 0;
  42. if (len != 296) {
  43. GPSD_LOG(LOG_PROG, &session->context->errout,
  44. "ITALK: bad NAV_FIX (len %zu, should be 296)\n",
  45. len);
  46. return -1;
  47. }
  48. flags = (unsigned short) getleu16(buf, 7 + 4);
  49. //cflags = (unsigned short) getleu16(buf, 7 + 6);
  50. pflags = (unsigned short) getleu16(buf, 7 + 8);
  51. session->newdata.status = STATUS_NO_FIX;
  52. session->newdata.mode = MODE_NO_FIX;
  53. mask = ONLINE_SET | MODE_SET | STATUS_SET | CLEAR_IS;
  54. /* just bail out if this fix is not marked valid */
  55. if (0 != (pflags & FIX_FLAG_MASK_INVALID)
  56. || 0 == (flags & FIXINFO_FLAG_VALID))
  57. return mask;
  58. tow = getleu32(buf, 7 + 84); /* tow in ms */
  59. MSTOTS(&ts_tow, tow);
  60. session->newdata.time = gpsd_gpstime_resolv(session,
  61. (unsigned short) getles16(buf, 7 + 82), ts_tow);
  62. mask |= TIME_SET | NTPTIME_IS;
  63. session->newdata.ecef.x = (double)(getles32(buf, 7 + 96) / 100.0);
  64. session->newdata.ecef.y = (double)(getles32(buf, 7 + 100) / 100.0);
  65. session->newdata.ecef.z = (double)(getles32(buf, 7 + 104) / 100.0);
  66. session->newdata.ecef.vx = (double)(getles32(buf, 7 + 186) / 1000.0);
  67. session->newdata.ecef.vy = (double)(getles32(buf, 7 + 190) / 1000.0);
  68. session->newdata.ecef.vz = (double)(getles32(buf, 7 + 194) / 1000.0);
  69. mask |= ECEF_SET | VECEF_SET;
  70. /* this eph does not look right, badly documented.
  71. * let gpsd_error_model() handle it
  72. * session->newdata.eph = (double)(getles32(buf, 7 + 252) / 100.0);
  73. */
  74. session->newdata.eps = (double)(getles32(buf, 7 + 254) / 100.0);
  75. /* compute epx/epy in gpsd_error_model(), not here */
  76. mask |= HERR_SET;
  77. #define MAX(a,b) (((a) > (b)) ? (a) : (b))
  78. session->gpsdata.satellites_used =
  79. (int)MAX(getleu16(buf, 7 + 12), getleu16(buf, 7 + 14));
  80. mask |= USED_IS;
  81. if (flags & FIX_CONV_DOP_VALID) {
  82. session->gpsdata.dop.hdop = (double)(getleu16(buf, 7 + 56) / 100.0);
  83. session->gpsdata.dop.gdop = (double)(getleu16(buf, 7 + 58) / 100.0);
  84. session->gpsdata.dop.pdop = (double)(getleu16(buf, 7 + 60) / 100.0);
  85. session->gpsdata.dop.vdop = (double)(getleu16(buf, 7 + 62) / 100.0);
  86. session->gpsdata.dop.tdop = (double)(getleu16(buf, 7 + 64) / 100.0);
  87. mask |= DOP_SET;
  88. }
  89. if ((pflags & FIX_FLAG_MASK_INVALID) == 0
  90. && (flags & FIXINFO_FLAG_VALID) != 0) {
  91. if (pflags & FIX_FLAG_3DFIX)
  92. session->newdata.mode = MODE_3D;
  93. else
  94. session->newdata.mode = MODE_2D;
  95. if (pflags & FIX_FLAG_DGPS_CORRECTION)
  96. session->newdata.status = STATUS_DGPS_FIX;
  97. else
  98. session->newdata.status = STATUS_FIX;
  99. }
  100. GPSD_LOG(LOG_DATA, &session->context->errout,
  101. "NAV_FIX: time=%s, ecef x:%.2f y:%.2f z:%.2f altHAE=%.2f "
  102. "speed=%.2f track=%.2f climb=%.2f mode=%d status=%d gdop=%.2f "
  103. "pdop=%.2f hdop=%.2f vdop=%.2f tdop=%.2f\n",
  104. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
  105. session->newdata.ecef.x,
  106. session->newdata.ecef.y, session->newdata.ecef.z,
  107. session->newdata.altHAE, session->newdata.speed,
  108. session->newdata.track, session->newdata.climb,
  109. session->newdata.mode, session->newdata.status,
  110. session->gpsdata.dop.gdop, session->gpsdata.dop.pdop,
  111. session->gpsdata.dop.hdop, session->gpsdata.dop.vdop,
  112. session->gpsdata.dop.tdop);
  113. return mask;
  114. }
  115. static gps_mask_t decode_itk_prnstatus(struct gps_device_t *session,
  116. unsigned char *buf, size_t len)
  117. {
  118. gps_mask_t mask;
  119. if (len < 62) {
  120. GPSD_LOG(LOG_PROG, &session->context->errout,
  121. "ITALK: runt PRN_STATUS (len=%zu)\n", len);
  122. mask = 0;
  123. } else {
  124. unsigned int i, nsv, nchan, st;
  125. uint32_t msec = getleu32(buf, 7 + 6);
  126. timespec_t ts_tow;
  127. char ts_buf[TIMESPEC_LEN];
  128. MSTOTS(&ts_tow, msec);
  129. session->gpsdata.skyview_time = gpsd_gpstime_resolv(session,
  130. (unsigned short)getleu16(buf, 7 + 4), ts_tow);
  131. gpsd_zero_satellites(&session->gpsdata);
  132. nchan = (unsigned int)getleu16(buf, 7 + 50);
  133. if (nchan > MAX_NR_VISIBLE_PRNS)
  134. nchan = MAX_NR_VISIBLE_PRNS;
  135. for (i = st = nsv = 0; i < nchan; i++) {
  136. unsigned int off = 7 + 52 + 10 * i;
  137. unsigned short flags;
  138. bool used;
  139. flags = (unsigned short) getleu16(buf, off);
  140. used = (bool)(flags & PRN_FLAG_USE_IN_NAV);
  141. session->gpsdata.skyview[st].PRN =
  142. (short)(getleu16(buf, off + 4) & 0xff);
  143. session->gpsdata.skyview[st].elevation =
  144. (double)(getles16(buf, off + 6) & 0xff);
  145. session->gpsdata.skyview[st].azimuth =
  146. (double)(getles16(buf, off + 8) & 0xff);
  147. session->gpsdata.skyview[st].ss =
  148. (double)(getleu16(buf, off + 2) & 0xff);
  149. session->gpsdata.skyview[st].used = used;
  150. if (session->gpsdata.skyview[st].PRN > 0) {
  151. st++;
  152. if (used)
  153. nsv++;
  154. }
  155. }
  156. session->gpsdata.satellites_visible = (int)st;
  157. session->gpsdata.satellites_used = (int)nsv;
  158. mask = USED_IS | SATELLITE_SET;;
  159. GPSD_LOG(LOG_DATA, &session->context->errout,
  160. "PRN_STATUS: time=%s visible=%d used=%d "
  161. "mask={USED|SATELLITE}\n",
  162. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
  163. session->gpsdata.satellites_visible,
  164. session->gpsdata.satellites_used);
  165. }
  166. return mask;
  167. }
  168. static gps_mask_t decode_itk_utcionomodel(struct gps_device_t *session,
  169. unsigned char *buf, size_t len)
  170. {
  171. int leap;
  172. unsigned short flags;
  173. timespec_t ts_tow;
  174. uint32_t tow; /* Time of week [ms] */
  175. char ts_buf[TIMESPEC_LEN];
  176. if (len != 64) {
  177. GPSD_LOG(LOG_PROG, &session->context->errout,
  178. "ITALK: bad UTC_IONO_MODEL (len %zu, should be 64)\n",
  179. len);
  180. return 0;
  181. }
  182. flags = (unsigned short) getleu16(buf, 7);
  183. if (0 == (flags & UTC_IONO_MODEL_UTCVALID))
  184. return 0;
  185. leap = (int)getleu16(buf, 7 + 24);
  186. if (session->context->leap_seconds < leap)
  187. session->context->leap_seconds = leap;
  188. tow = getleu32(buf, 7 + 38); /* in ms */
  189. MSTOTS(&ts_tow, tow);
  190. session->newdata.time = gpsd_gpstime_resolv(session,
  191. (unsigned short) getleu16(buf, 7 + 36), ts_tow);
  192. GPSD_LOG(LOG_DATA, &session->context->errout,
  193. "UTC_IONO_MODEL: time=%s mask={TIME}\n",
  194. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)));
  195. return TIME_SET | NTPTIME_IS;
  196. }
  197. static gps_mask_t decode_itk_subframe(struct gps_device_t *session,
  198. unsigned char *buf, size_t len)
  199. {
  200. unsigned short flags, prn, sf;
  201. unsigned int i;
  202. uint32_t words[10];
  203. if (len != 64) {
  204. GPSD_LOG(LOG_PROG, &session->context->errout,
  205. "ITALK: bad SUBFRAME (len %zu, should be 64)\n", len);
  206. return 0;
  207. }
  208. flags = (unsigned short) getleu16(buf, 7 + 4);
  209. prn = (unsigned short) getleu16(buf, 7 + 6);
  210. sf = (unsigned short) getleu16(buf, 7 + 8);
  211. GPSD_LOG(LOG_PROG, &session->context->errout,
  212. "iTalk 50B SUBFRAME prn %u sf %u - decode %s %s\n",
  213. prn, sf,
  214. (flags & SUBFRAME_WORD_FLAG_MASK) ? "error" : "ok",
  215. (flags & SUBFRAME_GPS_PREAMBLE_INVERTED) ? "(inverted)" : "");
  216. if (flags & SUBFRAME_WORD_FLAG_MASK)
  217. return 0; // don't try decode an erroneous packet
  218. /*
  219. * Timo says "SUBRAME message contains decoded navigation message subframe
  220. * words with parity checking done but parity bits still present."
  221. */
  222. for (i = 0; i < 10; i++)
  223. words[i] = (uint32_t)(getleu32(buf, 7 + 14 + 4 * i) >> 6) & 0xffffff;
  224. return gpsd_interpret_subframe(session, prn, words);
  225. }
  226. static gps_mask_t decode_itk_pseudo(struct gps_device_t *session,
  227. unsigned char *buf, size_t len)
  228. {
  229. unsigned short flags, n, i;
  230. unsigned int tow; /* time of week, in ms */
  231. timespec_t ts_tow;
  232. n = (unsigned short) getleu16(buf, 7 + 4);
  233. if ((n < 1) || (n > MAXCHANNELS)){
  234. GPSD_LOG(LOG_INF, &session->context->errout,
  235. "ITALK: bad PSEUDO channel count\n");
  236. return 0;
  237. }
  238. if (len != (size_t)((n+1)*36)) {
  239. GPSD_LOG(LOG_PROG, &session->context->errout,
  240. "ITALK: bad PSEUDO len %zu\n", len);
  241. }
  242. GPSD_LOG(LOG_PROG, &session->context->errout, "iTalk PSEUDO [%u]\n", n);
  243. flags = (unsigned short)getleu16(buf, 7 + 6);
  244. if ((flags & 0x3) != 0x3)
  245. return 0; // bail if measurement time not valid.
  246. tow = (unsigned int)getleu32(buf, 7 + 38);
  247. MSTOTS(&ts_tow, tow);
  248. session->newdata.time = gpsd_gpstime_resolv(session,
  249. (unsigned short int)getleu16((char *)buf, 7 + 8), ts_tow);
  250. session->gpsdata.raw.mtime = session->newdata.time;
  251. /* this is so we can tell which never got set */
  252. for (i = 0; i < MAXCHANNELS; i++)
  253. session->gpsdata.raw.meas[i].svid = 0;
  254. for (i = 0; i < n; i++){
  255. session->gpsdata.skyview[i].PRN =
  256. getleu16(buf, 7 + 26 + (i*36)) & 0xff;
  257. session->gpsdata.skyview[i].ss =
  258. getleu16(buf, 7 + 26 + (i*36 + 2)) & 0x3f;
  259. session->gpsdata.raw.meas[i].satstat =
  260. getleu32(buf, 7 + 26 + (i*36 + 4));
  261. session->gpsdata.raw.meas[i].pseudorange =
  262. getled64((char *)buf, 7 + 26 + (i*36 + 8));
  263. session->gpsdata.raw.meas[i].doppler =
  264. getled64((char *)buf, 7 + 26 + (i*36 + 16));
  265. session->gpsdata.raw.meas[i].carrierphase =
  266. getleu16(buf, 7 + 26 + (i*36 + 28));
  267. session->gpsdata.raw.meas[i].codephase = NAN;
  268. session->gpsdata.raw.meas[i].deltarange = NAN;
  269. }
  270. /* return RAW_IS; The above decode does not give reasonable results */
  271. return 0; /* do not report valid until decode is fixed */
  272. }
  273. static gps_mask_t italk_parse(struct gps_device_t *session,
  274. unsigned char *buf, size_t len)
  275. {
  276. unsigned int type;
  277. gps_mask_t mask = 0;
  278. if (len == 0)
  279. return 0;
  280. type = (unsigned int) getub(buf, 4);
  281. /* we may need to dump the raw packet */
  282. GPSD_LOG(LOG_RAW, &session->context->errout,
  283. "raw italk packet type 0x%02x\n", type);
  284. session->cycle_end_reliable = true;
  285. switch (type) {
  286. case ITALK_NAV_FIX:
  287. GPSD_LOG(LOG_DATA, &session->context->errout,
  288. "iTalk NAV_FIX len %zu\n", len);
  289. mask = decode_itk_navfix(session, buf, len) | (CLEAR_IS | REPORT_IS);
  290. break;
  291. case ITALK_PRN_STATUS:
  292. GPSD_LOG(LOG_DATA, &session->context->errout,
  293. "iTalk PRN_STATUS len %zu\n", len);
  294. mask = decode_itk_prnstatus(session, buf, len);
  295. break;
  296. case ITALK_UTC_IONO_MODEL:
  297. GPSD_LOG(LOG_DATA, &session->context->errout,
  298. "iTalk UTC_IONO_MODEL len %zu\n", len);
  299. mask = decode_itk_utcionomodel(session, buf, len);
  300. break;
  301. case ITALK_ACQ_DATA:
  302. GPSD_LOG(LOG_DATA, &session->context->errout,
  303. "iTalk ACQ_DATA len %zu\n", len);
  304. break;
  305. case ITALK_TRACK:
  306. GPSD_LOG(LOG_DATA, &session->context->errout,
  307. "iTalk TRACK len %zu\n", len);
  308. break;
  309. case ITALK_PSEUDO:
  310. GPSD_LOG(LOG_DATA, &session->context->errout,
  311. "iTalk PSEUDO len %zu\n", len);
  312. mask = decode_itk_pseudo(session, buf, len);
  313. break;
  314. case ITALK_RAW_ALMANAC:
  315. GPSD_LOG(LOG_DATA, &session->context->errout,
  316. "iTalk RAW_ALMANAC len %zu\n", len);
  317. break;
  318. case ITALK_RAW_EPHEMERIS:
  319. GPSD_LOG(LOG_DATA, &session->context->errout,
  320. "iTalk RAW_EPHEMERIS len %zu\n", len);
  321. break;
  322. case ITALK_SUBFRAME:
  323. mask = decode_itk_subframe(session, buf, len);
  324. break;
  325. case ITALK_BIT_STREAM:
  326. GPSD_LOG(LOG_DATA, &session->context->errout,
  327. "iTalk BIT_STREAM len %zu\n", len);
  328. break;
  329. case ITALK_AGC:
  330. case ITALK_SV_HEALTH:
  331. case ITALK_PRN_PRED:
  332. case ITALK_FREQ_PRED:
  333. case ITALK_DBGTRACE:
  334. case ITALK_START:
  335. case ITALK_STOP:
  336. case ITALK_SLEEP:
  337. case ITALK_STATUS:
  338. case ITALK_ITALK_CONF:
  339. case ITALK_SYSINFO:
  340. case ITALK_ITALK_TASK_ROUTE:
  341. case ITALK_PARAM_CTRL:
  342. case ITALK_PARAMS_CHANGED:
  343. case ITALK_START_COMPLETED:
  344. case ITALK_STOP_COMPLETED:
  345. case ITALK_LOG_CMD:
  346. case ITALK_SYSTEM_START:
  347. case ITALK_STOP_SEARCH:
  348. case ITALK_SEARCH:
  349. case ITALK_PRED_SEARCH:
  350. case ITALK_SEARCH_DONE:
  351. case ITALK_TRACK_DROP:
  352. case ITALK_TRACK_STATUS:
  353. case ITALK_HANDOVER_DATA:
  354. case ITALK_CORE_SYNC:
  355. case ITALK_WAAS_RAWDATA:
  356. case ITALK_ASSISTANCE:
  357. case ITALK_PULL_FIX:
  358. case ITALK_MEMCTRL:
  359. case ITALK_STOP_TASK:
  360. GPSD_LOG(LOG_DATA, &session->context->errout,
  361. "iTalk not processing packet: id 0x%02x length %zu\n",
  362. type, len);
  363. break;
  364. default:
  365. GPSD_LOG(LOG_DATA, &session->context->errout,
  366. "iTalk unknown packet: id 0x%02x length %zu\n",
  367. type, len);
  368. }
  369. return mask | ONLINE_SET;
  370. }
  371. static gps_mask_t italk_parse_input(struct gps_device_t *session)
  372. {
  373. if (session->lexer.type == ITALK_PACKET) {
  374. return italk_parse(session, session->lexer.outbuffer,
  375. session->lexer.outbuflen);;
  376. #ifdef NMEA0183_ENABLE
  377. } else if (session->lexer.type == NMEA_PACKET) {
  378. return nmea_parse((char *)session->lexer.outbuffer, session);
  379. #endif /* NMEA0183_ENABLE */
  380. } else
  381. return 0;
  382. }
  383. #ifdef __UNUSED__
  384. static void italk_ping(struct gps_device_t *session)
  385. /* send a "ping". it may help us detect an itrax more quickly */
  386. {
  387. char *ping = "<?>";
  388. (void)gpsd_write(session, ping, 3);
  389. }
  390. #endif /* __UNUSED__ */
  391. /* *INDENT-OFF* */
  392. const struct gps_type_t driver_italk =
  393. {
  394. .type_name = "iTalk", /* full name of type */
  395. .packet_type = ITALK_PACKET, /* associated lexer packet type */
  396. .flags = DRIVER_STICKY, /* no rollover or other flags */
  397. .trigger = NULL, /* recognize the type */
  398. .channels = 12, /* consumer-grade GPS */
  399. .probe_detect = NULL, /* how to detect at startup time */
  400. .get_packet = generic_get, /* use generic packet grabber */
  401. .parse_packet = italk_parse_input,/* parse message packets */
  402. .rtcm_writer = gpsd_write, /* send RTCM data straight */
  403. .init_query = NULL, /* non-perturbing initial query */
  404. .event_hook = NULL, /* lifetime event handler */
  405. .speed_switcher = NULL, /* no speed switcher */
  406. .mode_switcher = NULL, /* no mode switcher */
  407. .rate_switcher = NULL, /* no sample-rate switcher */
  408. .min_cycle.tv_sec = 1, /* not relevant, no rate switch */
  409. .min_cycle.tv_nsec = 0, /* not relevant, no rate switch */
  410. .control_send = NULL, /* no control string sender */
  411. .time_offset = NULL, /* no method for NTP fudge factor */
  412. };
  413. /* *INDENT-ON* */
  414. #endif /* defined(ITRAX_ENABLE) && defined(BINARY_ENABLE) */
  415. // vim: set expandtab shiftwidth=4