driver_oncore.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. /*
  2. * This file is Copyright 2010 by the GPSD project
  3. * SPDX-License-Identifier: BSD-2-clause
  4. */
  5. #include "../include/gpsd_config.h" // must be before all includes
  6. #include <stdbool.h>
  7. #include <stdio.h>
  8. #include "../include/gpsd.h"
  9. #if defined(ONCORE_ENABLE) && defined(BINARY_ENABLE)
  10. #include "../include/bits.h"
  11. #include "../include/timespec.h"
  12. static char enableEa[] = { 'E', 'a', 1 };
  13. static char enableBb[] = { 'B', 'b', 1 };
  14. static char getfirmware[] = { 'C', 'j' };
  15. /*static char enableEn[] =
  16. { 'E', 'n', 1, 0, 100, 100, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };*/
  17. /*static char enableAt2[] = { 'A', 't', 2, };*/
  18. static unsigned char pollAs[] =
  19. { 'A', 's', 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff,
  20. 0xff, 0xff, 0xff
  21. };
  22. static unsigned char pollAt[] = { 'A', 't', 0xff };
  23. static unsigned char pollAw[] = { 'A', 'w', 0xff };
  24. static unsigned char pollAy[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };
  25. static unsigned char pollBo[] = { 'B', 'o', 0x01 };
  26. static unsigned char pollEn[] = {
  27. 'E', 'n', 0xff, 0xff, 0xff, 0xff, 0xff,
  28. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
  29. };
  30. /*
  31. * These routines are specific to this driver
  32. */
  33. static gps_mask_t oncore_parse_input(struct gps_device_t *);
  34. static gps_mask_t oncore_dispatch(struct gps_device_t *, unsigned char *,
  35. size_t);
  36. static gps_mask_t oncore_msg_navsol(struct gps_device_t *, unsigned char *,
  37. size_t);
  38. static gps_mask_t oncore_msg_utc_offset(struct gps_device_t *,
  39. unsigned char *, size_t);
  40. static gps_mask_t oncore_msg_pos_hold_mode(struct gps_device_t *,
  41. unsigned char *, size_t);
  42. static gps_mask_t oncore_msg_pps_offset(struct gps_device_t *, unsigned char *,
  43. size_t);
  44. static gps_mask_t oncore_msg_svinfo(struct gps_device_t *, unsigned char *,
  45. size_t);
  46. static gps_mask_t oncore_msg_time_mode(struct gps_device_t *, unsigned char *,
  47. size_t);
  48. static gps_mask_t oncore_msg_time_raim(struct gps_device_t *, unsigned char *,
  49. size_t);
  50. static gps_mask_t oncore_msg_firmware(struct gps_device_t *, unsigned char *,
  51. size_t);
  52. /*
  53. * These methods may be called elsewhere in gpsd
  54. */
  55. static ssize_t oncore_control_send(struct gps_device_t *, char *, size_t);
  56. static void oncore_event_hook(struct gps_device_t *, event_t);
  57. /*
  58. * Decode the navigation solution message
  59. *
  60. * @@Ea - Position/Status/Data Message
  61. @@EamdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC
  62. <CR><LF>
  63. *
  64. * Date
  65. * m - month 1 .. 12
  66. * d - day 1 .. 31
  67. * yy - year 1980 .. 2079
  68. *
  69. * Time
  70. * h - hours 0 .. 23
  71. * m - minutes 0 .. 59
  72. * s - seconds 0 .. 60
  73. * ffff - fractional second 0 .. 999,999,999
  74. * (0.0 .. 0.999999999)
  75. *
  76. * Position
  77. * aaaa - latitude in mas -324,000,000 .. 324,000,000
  78. * (-90 degrees .. 90 degrees)
  79. *
  80. * oooo - longitude in mas -648,000,000 .. 648,000,000
  81. * (-180 degrees .. 180 degrees)
  82. *
  83. * hhhh - ellipsoid height in cm -100,000 .. 1,800,000
  84. * (-1000.00 .. 18,000.00m)*
  85. *
  86. * mmmm - not used 0
  87. *
  88. * Velocity
  89. * vv - velocity in cm/s 0 .. 51,400 (0..514.00 m/s)*
  90. * hh - heading 0 .. 3599 (0.0..359.9 degrees)
  91. *
  92. * (true north res 0.1 degrees)
  93. *
  94. * Geometry
  95. * dd - current DOP (0.1 res) 0 .. 999 (0.0 to 99.9 DOP)
  96. * (0 = not computable, position-hold, or position propagate)
  97. *
  98. * t - DOP TYPE
  99. * 0 PDOP (3D)/antenna ok
  100. * 1 PDOP (3D)/antenna OK
  101. * 64 PDOP (3D)/antenna shorted
  102. * 65 PDOP (3D)/antenna shorted
  103. * 128 PDOP (3D)/antenna open
  104. * 129 PDOP (3D)/antenna open
  105. * 192 PDOP (3D)/antenna shorted
  106. * 193 PDOP (3D)/antenna shorted
  107. *
  108. * Satellite visibility and tracking status
  109. * n - num of visible sats 0 .. 12
  110. * t - num of satellites tracked 0 .. 8
  111. *
  112. * For each of eight receiver channels
  113. * i - sat ID 0 .. 37
  114. * m - channel tracking mode 0 .. 8
  115. * 0 = code search 5 = message sync detect
  116. * 1 = code acquire 6 = satellite time avail.
  117. * 2 = AGC set 7 = ephemeris acquire
  118. * 3 = prep acquire 8 = avail for position
  119. * 4 = bit sync detect
  120. *
  121. * s - carrier to noise density ratio
  122. * (C/No) 0 .. 255 db-Hz
  123. *
  124. * d - channel status flag
  125. * Each bit represents one of the following:
  126. * (msb) Bit 7: using for position fix
  127. * Bit 6: satellite momentum alert flag
  128. * Bit 5: satellite anti-spoof flag set
  129. * Bit 4: satellite reported unhealthy
  130. * Bit 3: satellite reported inaccurate
  131. * (> 16m)
  132. * Bit 2: spare
  133. * Bit 1: spare
  134. * (lsb) Bit 0: parity error
  135. *
  136. * End of channel dependent data
  137. * s - receiver status flag
  138. *
  139. * Each bit represents one of the following:
  140. * (msb) Bit 7: position propagate mode
  141. * Bit 6: poor geometry (DOP > 12)
  142. * Bit 5: 3D fix
  143. * Bit 4: 2D fix
  144. * Bit 3: acquiring satellites/position hold
  145. * Bit 2: spare
  146. * Bit 1: insufficient visible satellites
  147. * (< 3)
  148. * (lsb) Bit 0: bad almanac
  149. *
  150. * C - checksum
  151. * Message length: 76 bytes
  152. *
  153. */
  154. static gps_mask_t
  155. oncore_msg_navsol(struct gps_device_t *session, unsigned char *buf,
  156. size_t data_len)
  157. {
  158. gps_mask_t mask;
  159. unsigned char flags;
  160. double lat, lon, alt;
  161. float speed, track, dop;
  162. unsigned int i, j, st, nsv;
  163. int Bbused;
  164. struct tm unpacked_date;
  165. char ts_buf[TIMESPEC_LEN];
  166. if (data_len != 76)
  167. return 0;
  168. mask = ONLINE_SET;
  169. GPSD_LOG(LOG_DATA, &session->context->errout,
  170. "oncore NAVSOL - navigation data\n");
  171. flags = (unsigned char)getub(buf, 72);
  172. if (flags & 0x20) {
  173. session->newdata.status = STATUS_GPS;
  174. session->newdata.mode = MODE_3D;
  175. } else if (flags & 0x10) {
  176. session->newdata.status = STATUS_GPS;
  177. session->newdata.mode = MODE_2D;
  178. } else if ((flags & 0x08) &&
  179. session->driver.oncore.pos_hold_mode ==
  180. ONCORE_POS_HOLD_MODE_ON) {
  181. /* To be a good time solution for a fixed surveyed position,
  182. * the poshold/acquiring flag (0x08) must be accompanied by
  183. * the device being in position hold-mode. The device is
  184. * also reporting acquiring (0x08) when it has no fix.
  185. * Therefore the pos_hold_mode check above.
  186. */
  187. session->newdata.status = STATUS_TIME;
  188. session->newdata.mode = MODE_3D;
  189. } else {
  190. GPSD_LOG(LOG_WARN, &session->context->errout,
  191. "oncore NAVSOL no fix - flags 0x%02x\n", flags);
  192. session->newdata.mode = MODE_NO_FIX;
  193. session->newdata.status = STATUS_UNK;
  194. }
  195. mask |= MODE_SET | STATUS_SET;
  196. /* Unless we have seen non-zero utc offset data, the time is GPS time
  197. * and not UTC time. Even if time mode (Aw) message already says UTC.
  198. * Do not use the time.
  199. */
  200. if (LEAP_SECOND_VALID == (session->context->valid & LEAP_SECOND_VALID)) {
  201. unsigned int nsec;
  202. unpacked_date.tm_mon = (int)getub(buf, 4) - 1;
  203. unpacked_date.tm_mday = (int)getub(buf, 5);
  204. unpacked_date.tm_year = (int)getbeu16(buf, 6) - 1900;
  205. unpacked_date.tm_hour = (int)getub(buf, 8);
  206. unpacked_date.tm_min = (int)getub(buf, 9);
  207. unpacked_date.tm_sec = (int)getub(buf, 10);
  208. unpacked_date.tm_isdst = 0;
  209. unpacked_date.tm_wday = unpacked_date.tm_yday = 0;
  210. nsec = (unsigned int) getbeu32(buf, 11);
  211. session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
  212. session->newdata.time.tv_nsec = nsec;
  213. mask |= TIME_SET;
  214. GPSD_LOG(LOG_DATA, &session->context->errout,
  215. "oncore NAVSOL - time: %04d-%02d-%02d %02d:%02d:%02d.%09d\n",
  216. unpacked_date.tm_year + 1900, unpacked_date.tm_mon + 1,
  217. unpacked_date.tm_mday, unpacked_date.tm_hour,
  218. unpacked_date.tm_min, unpacked_date.tm_sec, nsec);
  219. }
  220. lat = getbes32(buf, 15) / 3600000.0;
  221. lon = getbes32(buf, 19) / 3600000.0;
  222. alt = getbes32(buf, 23) / 100.0;
  223. speed = getbeu16(buf, 31) / 100.0f;
  224. track = getbeu16(buf, 33) / 10.0f;
  225. dop = getbeu16(buf, 35) / 10.0f;
  226. GPSD_LOG(LOG_DATA, &session->context->errout,
  227. "oncore NAVSOL - %lf %lf %.2lfm | %.2fm/s %.1fdeg dop=%.1f\n",
  228. lat, lon, alt, speed, track,
  229. (float)dop);
  230. if (MODE_2D <= session->newdata.mode) {
  231. session->newdata.latitude = lat;
  232. session->newdata.longitude = lon;
  233. session->newdata.speed = speed;
  234. session->newdata.track = track;
  235. mask |= LATLON_SET | SPEED_SET | TRACK_SET;
  236. }
  237. if (MODE_3D <= session->newdata.mode) {
  238. session->newdata.altHAE = alt; /* is WGS84 */
  239. mask |= ALTITUDE_SET;
  240. }
  241. gpsd_zero_satellites(&session->gpsdata);
  242. /* Merge the satellite information from the Bb message. */
  243. Bbused = 0;
  244. nsv = 0;
  245. for (i = st = 0; i < 8; i++) {
  246. int sv, mode, sn, status;
  247. unsigned int off;
  248. off = 40 + 4 * i;
  249. sv = (int)getub(buf, off);
  250. mode = (int)getub(buf, off + 1);
  251. sn = (int)getub(buf, off + 2);
  252. status = (int)getub(buf, off + 3);
  253. GPSD_LOG(LOG_DATA, &session->context->errout,
  254. "%2d %2d %2d %3d %02x\n", i, sv, mode, sn, status);
  255. if (sn) {
  256. session->gpsdata.skyview[st].PRN = (short)sv;
  257. session->gpsdata.skyview[st].ss = (double)sn;
  258. for (j = 0; (int)j < session->driver.oncore.visible; j++)
  259. if (session->driver.oncore.PRN[j] == sv) {
  260. session->gpsdata.skyview[st].elevation =
  261. (double)session->driver.oncore.elevation[j];
  262. session->gpsdata.skyview[st].azimuth =
  263. (double)session->driver.oncore.azimuth[j];
  264. Bbused |= 1 << j;
  265. break;
  266. }
  267. /* bit 7 of the status word: sat used for position */
  268. session->gpsdata.skyview[st].used = false;
  269. if (status & 0x80) {
  270. session->gpsdata.skyview[st].used = true;
  271. nsv++;
  272. }
  273. /* bit 2 of the status word: using for time solution */
  274. if (status & 0x02)
  275. mask |= NTPTIME_IS | GOODTIME_IS;
  276. /*
  277. * The GOODTIME_IS mask bit exists distinctly from TIME_SET exactly
  278. * so an OnCore running in time-service mode (and other GPS clocks)
  279. * can signal that it's returning time even though no position fixes
  280. * have been available.
  281. */
  282. st++;
  283. }
  284. }
  285. for (j = 0; (int)j < session->driver.oncore.visible; j++)
  286. if (!(Bbused & (1 << j))) {
  287. session->gpsdata.skyview[st].PRN =
  288. (short)session->driver.oncore.PRN[j];
  289. session->gpsdata.skyview[st].elevation =
  290. (double)session->driver.oncore.elevation[j];
  291. session->gpsdata.skyview[st].azimuth =
  292. (double)session->driver.oncore.azimuth[j];
  293. st++;
  294. }
  295. session->gpsdata.skyview_time = session->newdata.time;
  296. session->gpsdata.satellites_used = (int)nsv;
  297. session->gpsdata.satellites_visible = (int)st;
  298. mask |= SATELLITE_SET | USED_IS;
  299. /* Some messages can only be polled. As they are not so
  300. * important, would be enough to poll e.g. one message per cycle.
  301. */
  302. (void)oncore_control_send(session, (char *)pollAs, sizeof(pollAs));
  303. (void)oncore_control_send(session, (char *)pollAt, sizeof(pollAt));
  304. (void)oncore_control_send(session, (char *)pollAw, sizeof(pollAw));
  305. (void)oncore_control_send(session, (char *)pollAy, sizeof(pollAy));
  306. (void)oncore_control_send(session, (char *)pollBo, sizeof(pollBo));
  307. (void)oncore_control_send(session, (char *)pollEn, sizeof(pollEn));
  308. GPSD_LOG(LOG_DATA, &session->context->errout,
  309. "NAVSOL: time=%s lat=%.2f lon=%.2f altMSL=%.2f speed=%.2f "
  310. "track=%.2f mode=%d status=%d visible=%d used=%d\n",
  311. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
  312. session->newdata.latitude,
  313. session->newdata.longitude, session->newdata.altHAE,
  314. session->newdata.speed, session->newdata.track,
  315. session->newdata.mode, session->newdata.status,
  316. session->gpsdata.satellites_used,
  317. session->gpsdata.satellites_visible);
  318. return mask;
  319. }
  320. /**
  321. * GPS Leap Seconds = UTC offset
  322. */
  323. static gps_mask_t
  324. oncore_msg_utc_offset(struct gps_device_t *session, unsigned char *buf,
  325. size_t data_len)
  326. {
  327. int utc_offset;
  328. if (data_len != 8)
  329. return 0;
  330. utc_offset = (int)getub(buf, 4);
  331. GPSD_LOG(LOG_DATA, &session->context->errout,
  332. "oncore UTCTIME - leap seconds: %d\n", utc_offset);
  333. if (utc_offset == 0)
  334. return 0; /* that part of almanac not received yet */
  335. session->context->leap_seconds = utc_offset;
  336. session->context->valid |= LEAP_SECOND_VALID;
  337. return 0; /* no flag for leap seconds update */
  338. }
  339. /**
  340. * Pos hold mode
  341. *
  342. * @@AtmC<CR><LF>
  343. *
  344. * m - mode, 0=off, 1=on, 2=surveying
  345. */
  346. static gps_mask_t
  347. oncore_msg_pos_hold_mode(struct gps_device_t *session, unsigned char *buf,
  348. size_t data_len)
  349. {
  350. int pos_hold_mode;
  351. if (data_len != 8)
  352. return 0;
  353. pos_hold_mode = (int)getub(buf, 4);
  354. GPSD_LOG(LOG_DATA, &session->context->errout,
  355. "oncore pos hold mode: %d\n", pos_hold_mode);
  356. /* Add 1 to distinguish an unknown (not set) value from mode off. */
  357. session->driver.oncore.pos_hold_mode = pos_hold_mode + 1;
  358. return 0;
  359. }
  360. /**
  361. * PPS offset
  362. */
  363. static gps_mask_t
  364. oncore_msg_pps_offset(struct gps_device_t *session, unsigned char *buf,
  365. size_t data_len)
  366. {
  367. int pps_offset_ns;
  368. if (data_len != 11)
  369. return 0;
  370. pps_offset_ns = (int)getbes32(buf, 4);
  371. GPSD_LOG(LOG_DATA, &session->context->errout,
  372. "oncore PPS offset: %d\n", pps_offset_ns);
  373. session->driver.oncore.pps_offset_ns = pps_offset_ns;
  374. return 0;
  375. }
  376. /**
  377. * GPS Satellite Info
  378. */
  379. static gps_mask_t
  380. oncore_msg_svinfo(struct gps_device_t *session, unsigned char *buf,
  381. size_t data_len)
  382. {
  383. unsigned int i, nchan;
  384. if (data_len != 92)
  385. return 0;
  386. GPSD_LOG(LOG_DATA, &session->context->errout,
  387. "oncore SVINFO - satellite data\n");
  388. nchan = (unsigned int)getub(buf, 4);
  389. GPSD_LOG(LOG_DATA, &session->context->errout,
  390. "oncore SVINFO - %d satellites:\n", nchan);
  391. // Then we clamp the value to not read outside the table.
  392. if (nchan > 12)
  393. nchan = 12;
  394. session->driver.oncore.visible = (int)nchan;
  395. for (i = 0; i < nchan; i++) {
  396. // get info for one channel/satellite
  397. unsigned int off = 5 + 7 * i;
  398. int sv = (int)getub(buf, off);
  399. int el = (int)getub(buf, off + 3);
  400. int az = (int)getbeu16(buf, off + 4);
  401. GPSD_LOG(LOG_DATA, &session->context->errout,
  402. "%2d %2d %2d %3d\n", i, sv, el, az);
  403. // Store for use when Ea messages come.
  404. session->driver.oncore.PRN[i] = sv;
  405. session->driver.oncore.elevation[i] = (short)el;
  406. session->driver.oncore.azimuth[i] = (short)az;
  407. }
  408. return 0;
  409. }
  410. /**
  411. * GPS Time Mode
  412. *
  413. * @@AwmC<CR><LF>
  414. *
  415. * m - mode, 0=GPS, 1=UTC
  416. */
  417. static gps_mask_t oncore_msg_time_mode(struct gps_device_t *session UNUSED,
  418. unsigned char *buf UNUSED,
  419. size_t data_len UNUSED)
  420. {
  421. int time_mode;
  422. if (data_len != 8)
  423. return 0;
  424. time_mode = (int)getub(buf, 4);
  425. GPSD_LOG(LOG_DATA, &session->context->errout,
  426. "oncore time mode (0=GPS, 1=UTC): %d\n",time_mode);
  427. return 0;
  428. }
  429. /**
  430. * GPS Time RAIM
  431. */
  432. static gps_mask_t oncore_msg_time_raim(struct gps_device_t *session UNUSED,
  433. unsigned char *buf UNUSED,
  434. size_t data_len UNUSED)
  435. {
  436. int sawtooth_ns;
  437. if (data_len != 69)
  438. return 0;
  439. sawtooth_ns = (int)getub(buf, 25);
  440. GPSD_LOG(LOG_DATA, &session->context->errout,
  441. "oncore PPS sawtooth: %d\n",sawtooth_ns);
  442. // session->driver.oncore.traim_sawtooth_ns = sawtooth_ns;
  443. return 0;
  444. }
  445. /**
  446. * GPS Firmware
  447. */
  448. static gps_mask_t oncore_msg_firmware(struct gps_device_t *session UNUSED,
  449. unsigned char *buf UNUSED,
  450. size_t data_len UNUSED)
  451. {
  452. return 0;
  453. }
  454. #define ONCTYPE(id2,id3) ((((unsigned int)id2)<<8)|(id3))
  455. /**
  456. * Parse the data from the device
  457. */
  458. gps_mask_t oncore_dispatch(struct gps_device_t * session, unsigned char *buf,
  459. size_t len)
  460. {
  461. unsigned int type;
  462. if (0 == len) {
  463. return 0;
  464. }
  465. type = ONCTYPE(buf[2], buf[3]);
  466. // we may need to dump the raw packet
  467. GPSD_LOG(LOG_RAW, &session->context->errout,
  468. "raw Oncore packet type 0x%04x\n", type);
  469. session->cycle_end_reliable = true;
  470. switch (type) {
  471. case ONCTYPE('B', 'b'):
  472. return oncore_msg_svinfo(session, buf, len);
  473. case ONCTYPE('E', 'a'):
  474. return oncore_msg_navsol(session, buf, len) | (CLEAR_IS | REPORT_IS);
  475. case ONCTYPE('E', 'n'):
  476. return oncore_msg_time_raim(session, buf, len);
  477. case ONCTYPE('C', 'j'):
  478. return oncore_msg_firmware(session, buf, len);
  479. case ONCTYPE('B', 'o'):
  480. return oncore_msg_utc_offset(session, buf, len);
  481. case ONCTYPE('A', 's'):
  482. return 0; /* position hold position */
  483. case ONCTYPE('A', 't'):
  484. return oncore_msg_pos_hold_mode(session, buf, len);
  485. case ONCTYPE('A', 'w'):
  486. return oncore_msg_time_mode(session, buf, len);
  487. case ONCTYPE('A', 'y'):
  488. return oncore_msg_pps_offset(session, buf, len);
  489. default:
  490. /* FIX-ME: This gets noisy in a hurry. Change once your driver works */
  491. GPSD_LOG(LOG_WARN, &session->context->errout,
  492. "unknown packet id @@%c%c length %zd\n",
  493. type >> 8, type & 0xff, len);
  494. return 0;
  495. }
  496. }
  497. /**********************************************************
  498. *
  499. * Externally called routines below here
  500. *
  501. **********************************************************/
  502. /**
  503. * Write data to the device, doing any required padding or checksumming
  504. */
  505. static ssize_t oncore_control_send(struct gps_device_t *session,
  506. char *msg, size_t msglen)
  507. {
  508. size_t i;
  509. char checksum = 0;
  510. session->msgbuf[0] = '@';
  511. session->msgbuf[1] = '@';
  512. for (i = 0; i < msglen; i++) {
  513. checksum ^= session->msgbuf[i + 2] = msg[i];
  514. }
  515. session->msgbuf[msglen + 2] = checksum;
  516. session->msgbuf[msglen + 3] = '\r';
  517. session->msgbuf[msglen + 4] = '\n';
  518. session->msgbuflen = msglen + 5;
  519. GPSD_LOG(LOG_PROG, &session->context->errout,
  520. "writing oncore control type %c%c\n", msg[0], msg[1]);
  521. return gpsd_write(session, session->msgbuf, session->msgbuflen);
  522. }
  523. static void oncore_event_hook(struct gps_device_t *session, event_t event)
  524. {
  525. if (session->context->readonly)
  526. return;
  527. /*
  528. * Some oncore VP variants that have not been used after long
  529. * power-down will be silent on startup. Provoke
  530. * identification by requesting the firmware version.
  531. */
  532. if (event == event_wakeup)
  533. (void)oncore_control_send(session, getfirmware, sizeof(getfirmware));
  534. if (session->context->passive) {
  535. return;
  536. }
  537. /*
  538. * FIX-ME: It might not be necessary to call this on reactivate.
  539. * Experiment to see if the holds its settings through a close.
  540. */
  541. if (event == event_identified || event == event_reactivate) {
  542. (void)oncore_control_send(session, enableEa, sizeof(enableEa));
  543. (void)oncore_control_send(session, enableBb, sizeof(enableBb));
  544. /*(void)oncore_control_send(session, enableEn, sizeof(enableEn)); */
  545. /*(void)oncore_control_send(session,enableAt2,sizeof(enableAt2)); */
  546. /*(void)oncore_control_send(session,pollAs,sizeof(pollAs)); */
  547. (void)oncore_control_send(session, (char*)pollBo, sizeof(pollBo));
  548. }
  549. }
  550. static double oncore_time_offset(struct gps_device_t *session UNUSED)
  551. {
  552. /*
  553. * Only one sentence (NAVSOL) ships time. 0.175 seems best at
  554. * 9600 for UT+, not sure what the fudge should be at other baud
  555. * rates or for other models.
  556. */
  557. return 0.175;
  558. }
  559. static gps_mask_t oncore_parse_input(struct gps_device_t *session)
  560. {
  561. if (ONCORE_PACKET == session->lexer.type) {
  562. return oncore_dispatch(session, session->lexer.outbuffer,
  563. session->lexer.outbuflen);
  564. }
  565. if (NMEA_PACKET == session->lexer.type) {
  566. return nmea_parse((char *)session->lexer.outbuffer, session);
  567. }
  568. return 0;
  569. }
  570. // This is everything we export
  571. // *INDENT-OFF*
  572. const struct gps_type_t driver_oncore = {
  573. .type_name = "Motorola Oncore", // Full name of type
  574. .packet_type = ONCORE_PACKET, // numeric packet type
  575. .flags = DRIVER_STICKY, // remember this
  576. .trigger = NULL, // identifying response
  577. .channels = 12, // device channel count
  578. .probe_detect = NULL, // no probe
  579. .get_packet = generic_get, // packet getter
  580. .parse_packet = oncore_parse_input, // packet parser
  581. .rtcm_writer = gpsd_write, // device accepts RTCM
  582. .init_query = NULL, // non-perturbing query
  583. .event_hook = oncore_event_hook, // lifetime event hook
  584. .speed_switcher = NULL, // no speed setter
  585. .mode_switcher = NULL, // no mode setter
  586. .rate_switcher = NULL, // no speed setter
  587. .min_cycle.tv_sec = 1, // 1Hz
  588. .min_cycle.tv_nsec = 0,
  589. // Control string sender - should provide checksum and headers/trailer
  590. .control_send = oncore_control_send, // to send control strings
  591. .time_offset = oncore_time_offset, // NTP offset array
  592. };
  593. // *INDENT-ON*
  594. #endif // defined(ONCORE_ENABLE) && defined(BINARY_ENABLE)
  595. // vim: set expandtab shiftwidth=4