driver_navcom.c 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. /*
  2. * Driver for Navcom receivers using proprietary NCT messages,
  3. * a binary protocol.
  4. *
  5. * Vendor website: http://www.navcomtech.com/
  6. * Technical references: Technical Reference Manual P/N 96-3120001-3001
  7. *
  8. * Tested with two SF-2040G models
  9. *
  10. * At this stage, this driver implements the following commands:
  11. *
  12. * 0x20: Data Request (tell the unit which responses you want)
  13. * 0x3f: LED Configuration (controls the front panel LEDs -- for testing)
  14. * 0x1c: Test Support Block (again, blinks the front panel lights)
  15. *
  16. * and it understands the following responses:
  17. *
  18. * 0x06: Acknowledgement (without error)
  19. * 0x15: Negative Acknowledge
  20. * 0x86: Channel Status
  21. * 0xae: Identification Block
  22. * 0xb0: Raw Meas. Data Block
  23. * 0xb1: PVT Block
  24. * 0xb5: Pseudorange Noise Statistics
  25. * 0xd3: LBM DSP Status Block
  26. * 0xef: Clock Drift and Offset
  27. *
  28. * By Diego Berge. Contact via web form at http://www.navlost.eu/contact
  29. *
  30. * Week counters are not limited to 10 bits. It's unknown what
  31. * the firmware is doing to disambiguate them, if anything; it might just
  32. * be adding a fixed offset based on a hidden epoch value, in which case
  33. * unhappy things will occur on the next rollover.
  34. *
  35. * This file is Copyright 2010 by the GPSD project
  36. * SPDX-License-Identifier: BSD-2-clause
  37. */
  38. #include "gpsd_config.h" /* must be before all includes */
  39. #include <math.h>
  40. #include <stdbool.h>
  41. #include <stdio.h>
  42. #include <string.h>
  43. #include "gpsd.h"
  44. #if defined(NAVCOM_ENABLE)
  45. #include "bits.h"
  46. #include "timespec.h"
  47. /* Have data which is 24 bits long */
  48. #define getles3224(buf,off) (int32_t)(((uint32_t)getub((buf), (off)+2)<<24 | (uint32_t)getub((buf), (off)+1)<<16 | (uint32_t)getub((buf), (off))<<8)>>8)
  49. #define getleu3224(buf,off) (uint32_t)(((uint32_t)getub((buf), (off)+2)<<24 | (uint32_t)getub((buf), (off)+1)<<16 | (uint32_t)getub((buf), (off))<<8)>>8)
  50. /* And just to be difficult, Navcom is little endian but the GPS data stream
  51. is big endian. Some messages contain raw GPS data */
  52. #define getles16_be(buf, off) (int16_t)((((uint16_t)getub(buf, (off)) << 8) \
  53. | (uint16_t)getub(buf, (off)+1)))
  54. #define getleu16_be(buf, off) (uint16_t)((((uint16_t)getub(buf, (off)) << 8) \
  55. | (uint16_t)getub(buf, (off)+1)))
  56. #define getles32_be(buf, off) (int32_t)((((uint16_t)getleu16_be(buf, (off)) << 16) \
  57. | getleu16_be(buf, (off)+2)))
  58. #define getleu32_be(buf, off) (uint32_t)((((uint16_t)getleu16_be(buf, (off)) << 16) \
  59. | getleu16_be(buf, (off)+2)))
  60. #define getles64_be(buf, off) (int64_t)((((uint64_t)getleu32_be(buf, (off)) << 32) \
  61. | getleu32_be(buf, (off)+4)))
  62. #define getleu64_be(buf, off) (uint64_t)((((uint64_t)getleu32_be(buf, (off)) << 32) \
  63. | getleu32_be(buf, (off)+4)))
  64. #define getles3224_be(buf,off) (int32_t)(((uint32_t)getub((buf), (off))<<24 \
  65. | (uint32_t)getub((buf), (off)+1)<<16 \
  66. | (uint32_t)getub((buf), (off)+2)<<8)>>8)
  67. #define NAVCOM_CHANNELS 12
  68. static uint8_t checksum(unsigned char *buf, size_t len)
  69. {
  70. size_t n;
  71. uint8_t csum = (uint8_t) 0x00;
  72. for (n = 0; n < len; n++)
  73. csum ^= buf[n];
  74. return csum;
  75. }
  76. static bool navcom_send_cmd(struct gps_device_t *session, unsigned char *cmd,
  77. size_t len)
  78. {
  79. return (gpsd_write(session, (const char *)cmd, len) == (ssize_t) len);
  80. }
  81. /* Data Request */
  82. static void navcom_cmd_0x20(struct gps_device_t *session, uint8_t block_id,
  83. uint16_t rate)
  84. {
  85. unsigned char msg[18];
  86. putbyte(msg, 0, 0x02);
  87. putbyte(msg, 1, 0x99);
  88. putbyte(msg, 2, 0x66);
  89. putbyte(msg, 3, 0x20); /* Cmd ID */
  90. putle16(msg, 4, 0x000e); /* Length */
  91. putbyte(msg, 6, 0x00); /* Action */
  92. putbyte(msg, 7, 0x01); /* Count of blocks */
  93. putbyte(msg, 8, block_id); /* Data Block ID */
  94. putbyte(msg, 9, 0x02); /* Logical Ports */
  95. putle16(msg, 10, rate); /* Data rate */
  96. putbyte(msg, 12, 0x71);
  97. putbyte(msg, 13, 0x00);
  98. putle16(msg, 14, 0x0000);
  99. putbyte(msg, 16, checksum(msg + 3, 13));
  100. putbyte(msg, 17, 0x03);
  101. (void)navcom_send_cmd(session, msg, 18);
  102. GPSD_LOG(LOG_PROG, &session->context->errout,
  103. "Navcom: sent command 0x20 (Data Request) "
  104. "- data block id = %02x at rate %02x\n", block_id, rate);
  105. }
  106. // cppcheck-suppress unusedFunction
  107. static void UNUSED navcom_cmd_0x3f(struct gps_device_t *session)
  108. /* Changes the LED settings in the receiver */
  109. {
  110. unsigned char msg[12];
  111. putbyte(msg, 0, 0x02);
  112. putbyte(msg, 1, 0x99);
  113. putbyte(msg, 2, 0x66);
  114. putbyte(msg, 3, 0x3f); /* Cmd ID */
  115. putle16(msg, 4, 0x0008);
  116. putbyte(msg, 6, 0x01); /* Action */
  117. putbyte(msg, 7, 0x00); /* Reserved */
  118. putbyte(msg, 8, 0x02); /* Link LED setting */
  119. putbyte(msg, 9, 0x0a); /* Battery LED setting */
  120. putbyte(msg, 10, checksum(msg + 3, 7));
  121. putbyte(msg, 11, 0x03);
  122. (void)navcom_send_cmd(session, msg, 12);
  123. GPSD_LOG(LOG_PROG, &session->context->errout,
  124. "Navcom: sent command 0x3f (LED Configuration Block)\n");
  125. }
  126. /* Test Support Block - Blinks the LEDs */
  127. static void navcom_cmd_0x1c(struct gps_device_t *session, uint8_t mode,
  128. uint8_t length)
  129. {
  130. unsigned char msg[12];
  131. putbyte(msg, 0, 0x02);
  132. putbyte(msg, 1, 0x99);
  133. putbyte(msg, 2, 0x66);
  134. putbyte(msg, 3, 0x1c); /* Cmd ID */
  135. putle16(msg, 4, 0x0008);
  136. putbyte(msg, 6, 0x04); /* Use ACK/NAK */
  137. putbyte(msg, 7, mode); /* 0x01 or 0x02 */
  138. putbyte(msg, 8, length); /* Only if mode == 0x01 */
  139. putbyte(msg, 9, 0x00);
  140. putbyte(msg, 10, checksum(msg + 3, 7));
  141. putbyte(msg, 11, 0x03);
  142. (void)navcom_send_cmd(session, msg, 12);
  143. GPSD_LOG(LOG_PROG, &session->context->errout,
  144. "Navcom: sent command 0x1c (Test Support Block)\n");
  145. GPSD_LOG(LOG_DATA, &session->context->errout,
  146. "Navcom: command 0x1c mode = %02x, length = %u\n",
  147. mode, length);
  148. }
  149. /* Serial Port Configuration */
  150. static void navcom_cmd_0x11(struct gps_device_t *session,
  151. uint8_t port_selection)
  152. {
  153. /* NOTE - We only allow changing one port at a time,
  154. * although the message supports doing both at once. */
  155. unsigned char msg[12];
  156. putbyte(msg, 0, 0x02);
  157. putbyte(msg, 1, 0x99);
  158. putbyte(msg, 2, 0x66);
  159. putbyte(msg, 3, 0x11); /* Cmd ID */
  160. putle16(msg, 4, 0x0008); /* Length */
  161. putbyte(msg, 6, 0x04); /* Action - Use ACK/NAK) */
  162. putbyte(msg, 7, port_selection);
  163. putbyte(msg, 8, 0x00); /* Reserved */
  164. putbyte(msg, 9, 0x00); /* Reserved */
  165. putbyte(msg, 10, checksum(msg + 3, 7));
  166. putbyte(msg, 11, 0x03);
  167. (void)navcom_send_cmd(session, msg, 12);
  168. GPSD_LOG(LOG_PROG, &session->context->errout,
  169. "Navcom: sent command 0x11 (Serial Port Configuration)\n");
  170. GPSD_LOG(LOG_DATA, &session->context->errout,
  171. "Navcom: serial port selection: 0x%02x\n", port_selection);
  172. }
  173. static void navcom_event_hook(struct gps_device_t *session, event_t event)
  174. {
  175. if (session->context->readonly)
  176. return;
  177. /* Request the following messages: */
  178. if (event == event_identified) {
  179. /* NOTE - Channel Status allows us to know into which of the
  180. * unit's various serial ports we are connected.
  181. * Its value gets updated every time we receive a 0x06 (Ack)
  182. * message. Note that if commands are being fed into the
  183. * unit from more than one port (which is entirely possible
  184. * although not necessarily a bright idea), there is a good
  185. * chance that we might misidentify our port */
  186. navcom_cmd_0x1c(session, 0x02, 0); /* Test Support Block */
  187. navcom_cmd_0x20(session, 0xae, 0x0000); /* Identification Block */
  188. navcom_cmd_0x20(session, 0x86, 0x000a); /* Channel Status */
  189. navcom_cmd_0x1c(session, 0x01, 5); /* Blink LEDs on receiver */
  190. navcom_cmd_0x20(session, 0xae, 0x1770); /* Identification Block - send every 10 min */
  191. navcom_cmd_0x20(session, 0xb1, 0x4000); /* PVT Block */
  192. navcom_cmd_0x20(session, 0xb5, 0x00c8); /* Pseudorange Noise Statistics - send every 20s */
  193. navcom_cmd_0x20(session, 0xb0, 0x4000); /* Raw Meas Data Block */
  194. navcom_cmd_0x20(session, 0x81, 0x0000); /* Packed Ephemeris Data - send once */
  195. navcom_cmd_0x20(session, 0x81, 0x4000); /* Packed Ephemeris Data */
  196. navcom_cmd_0x20(session, 0x86, 0x4000); /* Channel Status */
  197. navcom_cmd_0x20(session, 0x83, 0x4000); /* Ionosphere and UTC Data */
  198. navcom_cmd_0x20(session, 0xef, 0x0bb8); /* Clock Drift - send every 5 min */
  199. }
  200. }
  201. /* Ionosphere and UTC Data */
  202. static gps_mask_t handle_0x83(struct gps_device_t *session)
  203. {
  204. /* NOTE - At the present moment this is only being used
  205. * for determining the GPS-UTC time difference,
  206. * for which the iono data is not needed as far
  207. * as we are concerned. However, I am still
  208. * reporting it (if debuglevel >= LOG_DATA) as a
  209. * matter of interest */
  210. /* 2^-30 */
  211. #define SF_A0 (0.000000000931322574615478515625)
  212. /* 2^-50 */
  213. #define SF_A1 (0.000000000000000888178419700125)
  214. /* 2^12 */
  215. #define SF_TOT (4096)
  216. /* 2^-30 */
  217. #define SF_ALPHA0 (0.000000000931322574615478515625)
  218. /* 2^-27 */
  219. #define SF_ALPHA1 (0.000000007450580596923828125)
  220. /* 2^-24 */
  221. #define SF_ALPHA2 (0.000000059604644775390625)
  222. /* 2^-24 */
  223. #define SF_ALPHA3 (0.000000059604644775390625)
  224. /* 2^11 */
  225. #define SF_BETA0 (2048)
  226. /* 2^14 */
  227. #define SF_BETA1 (16384)
  228. /* 2^16 */
  229. #define SF_BETA2 (65536)
  230. /* 2^16 */
  231. #define SF_BETA3 (65536)
  232. unsigned char *buf = session->lexer.outbuffer + 3;
  233. uint16_t week = getleu16(buf, 3);
  234. uint32_t tow = getleu32(buf, 5);
  235. int8_t alpha0 = getsb(buf, 9);
  236. int8_t alpha1 = getsb(buf, 10);
  237. int8_t alpha2 = getsb(buf, 11);
  238. int8_t alpha3 = getsb(buf, 12);
  239. int8_t beta0 = getsb(buf, 13);
  240. int8_t beta1 = getsb(buf, 14);
  241. int8_t beta2 = getsb(buf, 15);
  242. int8_t beta3 = getsb(buf, 16);
  243. int32_t a1 = getles32(buf, 17);
  244. int32_t a0 = getles32(buf, 21);
  245. uint8_t tot = getub(buf, 25);
  246. uint8_t wnt = getub(buf, 26);
  247. int8_t dtls = getsb(buf, 27);
  248. uint8_t wnlsf = getub(buf, 28);
  249. uint8_t dn = getub(buf, 29);
  250. int8_t dtlsf = getsb(buf, 30);
  251. /* Ref.: ICD-GPS-200C 20.3.3.5.2.4 */
  252. if ((week % 256) * 604800 + tow / 1000.0 < wnlsf * 604800 + dn * 86400) {
  253. /* Effectivity time is in the future, use dtls */
  254. session->context->leap_seconds = (int)dtls;
  255. } else {
  256. /* Effectivity time is not in the future, use dtlsf */
  257. session->context->leap_seconds = (int)dtlsf;
  258. }
  259. GPSD_LOG(LOG_PROG, &session->context->errout,
  260. "Navcom: received packet type 0x83 (Ionosphere and UTC Data)\n");
  261. GPSD_LOG(LOG_DATA, &session->context->errout,
  262. "Navcom: Scaled parameters follow:\n");
  263. GPSD_LOG(LOG_DATA, &session->context->errout,
  264. "Navcom: GPS Week: %u, GPS Time of Week: %u (GPS Time: %f)\n",
  265. week, tow, week * 604800 + tow / 1000.0);
  266. GPSD_LOG(LOG_DATA, &session->context->errout,
  267. "Navcom: a0: %12.4E, a1: %12.4E, a2: %12.4E, a3: %12.4E, "
  268. "b0: %12.4E, b1: %12.4E, b2: %12.4E, b3: %12.4E\n",
  269. (double)alpha0 * SF_ALPHA0, (double)alpha1 * SF_ALPHA1,
  270. (double)alpha2 * SF_ALPHA2, (double)alpha3 * SF_ALPHA3,
  271. (double)beta0 * SF_BETA0, (double)beta1 * SF_BETA1,
  272. (double)beta2 * SF_BETA2, (double)beta3 * SF_BETA3);
  273. GPSD_LOG(LOG_DATA, &session->context->errout,
  274. "Navcom: A0: %19.12E, A1: %19.12E\n", (double)a0 * SF_A0,
  275. (double)a1 * SF_A1);
  276. GPSD_LOG(LOG_DATA, &session->context->errout,
  277. "Navcom: UTC Ref. Time: %lu, UTC Ref. Week: %u, dTls: %d\n",
  278. (unsigned long)tot * SF_TOT, wnt, dtls);
  279. GPSD_LOG(LOG_DATA, &session->context->errout,
  280. "Navcom: Week of leap seconds: %u, Day number of leap seconds: %u, dTlsf: %d\n",
  281. wnlsf, dn, dtlsf);
  282. return 0; /* No flag for update of leap seconds (Not part of a fix) */
  283. #undef SF_A0
  284. #undef SF_A1
  285. #undef SF_TOT
  286. #undef SF_ALPHA0
  287. #undef SF_ALPHA1
  288. #undef SF_ALPHA2
  289. #undef SF_ALPHA3
  290. #undef SF_BETA0
  291. #undef SF_BETA1
  292. #undef SF_BETA2
  293. #undef SF_BETA3
  294. }
  295. /* Acknowledgement (without error) */
  296. static gps_mask_t handle_0x06(struct gps_device_t *session)
  297. {
  298. unsigned char *buf = session->lexer.outbuffer + 3;
  299. uint8_t cmd_id = getub(buf, 3);
  300. uint8_t port = getub(buf, 4);
  301. session->driver.navcom.physical_port = port; /* This tells us which serial port was used last */
  302. GPSD_LOG(LOG_PROG, &session->context->errout,
  303. "Navcom: received packet type 0x06 (Acknowledgement (without error))\n");
  304. GPSD_LOG(LOG_DATA, &session->context->errout,
  305. "Navcom: acknowledged command id 0x%02x on port %c\n",
  306. cmd_id, (port == 0 ? 'A' : (port == 1 ? 'B' : '?')));
  307. return 0; /* Nothing updated */
  308. }
  309. /* Negative Acknowledge */
  310. static gps_mask_t handle_0x15(struct gps_device_t *session)
  311. {
  312. unsigned n;
  313. unsigned char *buf = session->lexer.outbuffer + 3;
  314. unsigned msg_len = getleu16(buf, 1);
  315. uint8_t port, cmd_id = getub(buf, 3);
  316. GPSD_LOG(LOG_PROG, &session->context->errout,
  317. "Navcom: received packet type 0x15 (Negative Acknowledge)\n");
  318. if (18 < msg_len) {
  319. GPSD_LOG(LOG_PROG, &session->context->errout,
  320. "Navcom: 0x15 too long %u\n", msg_len);
  321. return 0;
  322. }
  323. for (n = 4; n < (msg_len - 2); n += 2) {
  324. uint8_t err_id = getub(buf, n);
  325. uint8_t err_desc = getub(buf, n + 1);
  326. GPSD_LOG(LOG_DATA, &session->context->errout,
  327. "Navcom: error id = 0x%02x, error description = 0x%02x\n",
  328. err_id, err_desc);
  329. }
  330. port = getub(buf, n);
  331. GPSD_LOG(LOG_DATA, &session->context->errout,
  332. "Navcom: negative acknowledge was for command id 0x%02x "
  333. "on port %c\n",
  334. cmd_id, (port == 0 ? 'A' : (port == 1 ? 'B' : '?')));
  335. return 0; /* Nothing updated */
  336. }
  337. /* PVT Block */
  338. static gps_mask_t handle_0xb1(struct gps_device_t *session)
  339. {
  340. gps_mask_t mask;
  341. unsigned char *buf = session->lexer.outbuffer + 3;
  342. uint16_t week;
  343. uint32_t tow;
  344. timespec_t ts_tow;
  345. int32_t lat, lon;
  346. char ts_buf[TIMESPEC_LEN];
  347. /* Resolution of lat/lon values (2^-11) */
  348. #define LL_RES (0.00048828125)
  349. uint8_t lat_fraction, lon_fraction;
  350. /* Resolution of lat/lon fractions (2^-15) */
  351. #define LL_FRAC_RES (0.000030517578125)
  352. uint8_t nav_mode;
  353. int32_t ellips_height, altitude;
  354. /* Resolution of height and altitude values (2.0^-10) */
  355. #define EL_RES (0.0009765625)
  356. double vel_north, vel_east, vel_up;
  357. uint8_t gdop, pdop, hdop, vdop, tdop;
  358. /* This value means "undefined" */
  359. #define DOP_UNDEFINED (255)
  360. int16_t ant_height_adj;
  361. int32_t set_delta_up;
  362. /* Resolution of delta north, east, and up,
  363. * and ant. height adjustment values (1mm) */
  364. #define D_RES (0.001)
  365. #ifdef __UNUSED__
  366. /* Other values provided by the PVT block which we
  367. * may want to provide in the future.
  368. */
  369. uint8_t dgps_conf;
  370. uint16_t max_dgps_age;
  371. uint8_t ext_nav_mode;
  372. int32_t set_delta_north, set_delta_east;
  373. uint8_t nav_failure_code;
  374. #endif /* __UNUSED__ */
  375. /* Timestamp */
  376. week = (uint16_t) getleu16(buf, 3);
  377. tow = (uint32_t) getleu32(buf, 5); /* tow in ms */
  378. MSTOTS(&ts_tow, tow);
  379. session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
  380. /* Get latitude, longitude */
  381. lat = getles32(buf, 13);
  382. lon = getles32(buf, 17);
  383. lat_fraction = (uint8_t) (getub(buf, 21) >> 4);
  384. lon_fraction = (uint8_t) (getub(buf, 21) & 0x0f);
  385. session->newdata.latitude =
  386. (double)(lat * LL_RES + lat_fraction * LL_FRAC_RES) / 3600;
  387. session->newdata.longitude =
  388. (double)(lon * LL_RES + lon_fraction * LL_FRAC_RES) / 3600;
  389. /* Nav mode */
  390. nav_mode = (uint8_t) getub(buf, 22);
  391. if (-nav_mode & 0x80) {
  392. session->newdata.status = STATUS_NO_FIX;
  393. session->newdata.mode = MODE_NO_FIX;
  394. } else {
  395. session->newdata.mode = ((nav_mode & 0x40)!=0 ? MODE_3D : MODE_2D);
  396. session->newdata.status =
  397. ((nav_mode & 0x03)!=0 ? STATUS_DGPS_FIX : STATUS_FIX);
  398. }
  399. /* altHAE */
  400. ellips_height = getles32(buf, 23);
  401. /* altMSL */
  402. altitude = getles32(buf, 27);
  403. ant_height_adj = getles16(buf, 51);
  404. set_delta_up = getles32(buf, 79);
  405. session->newdata.altMSL = (double)(altitude * EL_RES)
  406. + (ant_height_adj * D_RES) + (set_delta_up * D_RES);
  407. session->newdata.altHAE = (double)(ellips_height) * EL_RES
  408. + (ant_height_adj * D_RES) + (set_delta_up * D_RES);
  409. /* Let gpsd_error_model() deal with geoid_sep */
  410. /* Speed Data */
  411. vel_north = (double)getles3224(buf, 31);
  412. vel_east = (double)getles3224(buf, 34);
  413. vel_up = (double)getles3224(buf, 37);
  414. session->newdata.NED.velN = vel_north * 0.1;
  415. session->newdata.NED.velE = vel_east * 0.1;
  416. session->newdata.NED.velD = -vel_up * 0.1;
  417. /* Quality indicators */
  418. /* UNUSED fom = getub(buf, 40); * FOM is DRMS */
  419. gdop = getub(buf, 41);
  420. pdop = getub(buf, 42);
  421. hdop = getub(buf, 43);
  422. vdop = getub(buf, 44);
  423. tdop = getub(buf, 45);
  424. /* UNUSED tfom = getub(buf, 46); * tfom == 10 * TDOP */
  425. /* let gpsd_error_model() do the error estimates */
  426. if (gdop != DOP_UNDEFINED)
  427. session->gpsdata.dop.gdop = gdop / 10.0;
  428. if (pdop != DOP_UNDEFINED)
  429. session->gpsdata.dop.pdop = pdop / 10.0;
  430. if (hdop != DOP_UNDEFINED)
  431. session->gpsdata.dop.hdop = hdop / 10.0;
  432. if (vdop != DOP_UNDEFINED)
  433. session->gpsdata.dop.vdop = vdop / 10.0;
  434. if (tdop != DOP_UNDEFINED)
  435. session->gpsdata.dop.tdop = tdop / 10.0;
  436. GPSD_LOG(LOG_PROG, &session->context->errout,
  437. "Navcom: received packet type 0xb1 (PVT Report)\n");
  438. GPSD_LOG(LOG_DATA, &session->context->errout,
  439. "Navcom: navigation mode %s (0x%02x) - %s - %s\n",
  440. ((-nav_mode & 0x80)!='\0' ? "invalid" : "valid"), nav_mode,
  441. ((nav_mode & 0x40)!='\0' ? "3D" : "2D"),
  442. ((nav_mode & 0x03)!='\0' ? "DGPS" : "GPS"));
  443. GPSD_LOG(LOG_DATA, &session->context->errout,
  444. "Navcom: velocities: north = %f east = %f up = %f\n",
  445. session->newdata.NED.velN,
  446. session->newdata.NED.velE,
  447. -session->newdata.NED.velD);
  448. #undef D_RES
  449. #undef LL_RES
  450. #undef LL_FRAC_RES
  451. #undef EL_RES
  452. #undef VEL_RES
  453. #undef DOP_UNDEFINED
  454. mask = LATLON_SET | ALTITUDE_SET | STATUS_SET | MODE_SET | USED_IS |
  455. HERR_SET | TIMERR_SET | DOP_SET | VNED_SET | TIME_SET | NTPTIME_IS;
  456. GPSD_LOG(LOG_DATA, &session->context->errout,
  457. "PVT 0xb1: time=%s, lat=%.2f lon=%.2f altHAE=%.2f "
  458. "altMSL %.2f mode=%d status=%d gdop=%.2f pdop=%.2f hdop=%.2f "
  459. "vdop=%.2f tdop=%.2f mask={%s}\n",
  460. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
  461. session->newdata.latitude,
  462. session->newdata.longitude,
  463. session->newdata.altHAE,
  464. session->newdata.altMSL,
  465. session->newdata.mode,
  466. session->newdata.status,
  467. session->gpsdata.dop.gdop,
  468. session->gpsdata.dop.pdop,
  469. session->gpsdata.dop.hdop,
  470. session->gpsdata.dop.vdop, session->gpsdata.dop.tdop,
  471. gps_maskdump(mask));
  472. return mask;
  473. }
  474. /* Packed Ephemeris Data */
  475. static gps_mask_t handle_0x81(struct gps_device_t *session)
  476. {
  477. /* Scale factors for everything */
  478. /* 2^-31 */
  479. #define SF_TGD (.000000000465661287307739257812)
  480. /* 2^4 */
  481. #define SF_TOC (16)
  482. /* 2^-55 */
  483. #define SF_AF2 (.000000000000000027755575615628)
  484. /* 2^-43 */
  485. #define SF_AF1 (.000000000000113686837721616029)
  486. /* 2^-31 */
  487. #define SF_AF0 (.000000000465661287307739257812)
  488. /* 2^-5 */
  489. #define SF_CRS (.031250000000000000000000000000)
  490. /* 2^-43 */
  491. #define SF_DELTA_N (.000000000000113686837721616029)
  492. /* 2^-31 */
  493. #define SF_M0 (.000000000465661287307739257812)
  494. /* 2^-29 */
  495. #define SF_CUC (.000000001862645149230957031250)
  496. /* 2^-33 */
  497. #define SF_E (.000000000116415321826934814453)
  498. /* 2^-29 */
  499. #define SF_CUS (.000000001862645149230957031250)
  500. /* 2^-19 */
  501. #define SF_SQRT_A (.000001907348632812500000000000)
  502. /* 2^4 */
  503. #define SF_TOE (16)
  504. /* 2^-29 */
  505. #define SF_CIC (.000000001862645149230957031250)
  506. /* 2^-31 */
  507. #define SF_OMEGA0 (.000000000465661287307739257812)
  508. /* 2^-29 */
  509. #define SF_CIS (.000000001862645149230957031250)
  510. /* 2^-31 */
  511. #define SF_I0 (.000000000465661287307739257812)
  512. /* 2^-5 */
  513. #define SF_CRC (.031250000000000000000000000000)
  514. /* 2^-31 */
  515. #define SF_OMEGA (.000000000465661287307739257812)
  516. /* 2^-43 */
  517. #define SF_OMEGADOT (.000000000000113686837721616029)
  518. /* 2^-43 */
  519. #define SF_IDOT (.000000000000113686837721616029)
  520. char ts_buf[TIMESPEC_LEN];
  521. unsigned char *buf = session->lexer.outbuffer + 3;
  522. uint8_t prn = getub(buf, 3);
  523. uint16_t week = getleu16(buf, 4);
  524. uint32_t tow = getleu32(buf, 6);
  525. uint16_t iodc = getleu16(buf, 10);
  526. /* And now the fun starts... everything that follows is
  527. * raw GPS data minus parity */
  528. /* Subframe 1, words 3 to 10 minus parity */
  529. uint16_t wn = (getleu16_be(buf, 12) & 0xffc0) >> 6;
  530. uint8_t cl2 = (getub(buf, 13) & 0x30) >> 4;
  531. uint8_t ura = getub(buf, 13) & 0x0f;
  532. uint8_t svh = (getub(buf, 14) & 0xfc) >> 2;
  533. /* We already have IODC from earlier in the message, so
  534. * we do not decode again */
  535. /* uint16_t iodc = (getub(buf, 14)&0x03)<<8;*/
  536. uint8_t l2pd = (getub(buf, 15) & 0x80) >> 7;
  537. int8_t tgd = getsb(buf, 26);
  538. /* iodc |= getub(buf, 27);*/
  539. uint16_t toc = getleu16_be(buf, 28);
  540. int8_t af2 = getsb(buf, 30);
  541. int16_t af1 = getles16_be(buf, 31);
  542. int32_t af0 = getles3224_be(buf, 33) >> 2;
  543. /* Subframe 2, words 3 to 10 minus parity */
  544. uint8_t iode = getub(buf, 36);
  545. int16_t crs = getles16_be(buf, 37);
  546. int16_t delta_n = getles16_be(buf, 39);
  547. int32_t m0 = getles32_be(buf, 41);
  548. int16_t cuc = getles16_be(buf, 45);
  549. uint32_t e = getleu32_be(buf, 47);
  550. int16_t cus = getles16_be(buf, 51);
  551. uint32_t sqrt_a = getleu32_be(buf, 53);
  552. uint16_t toe = getleu16_be(buf, 57);
  553. /* NOTE - Fit interval & AODO not collected */
  554. /* Subframe 3, words 3 to 10 minus parity */
  555. int16_t cic = getles16_be(buf, 60);
  556. int32_t Omega0 = getles32_be(buf, 62);
  557. int16_t cis = getles16_be(buf, 66);
  558. int32_t i0 = getles32_be(buf, 68);
  559. int16_t crc = getles16_be(buf, 72);
  560. int32_t omega = getles32_be(buf, 74);
  561. int32_t Omegadot = getles3224_be(buf, 78);
  562. /* Question: What is the proper way of shifting a signed int 2 bits to
  563. * the right, preserving sign? Answer: integer division by 4. */
  564. int16_t idot = (int16_t) (((getles16_be(buf, 82) & 0xfffc) / 4) |
  565. ((getub(buf, 82) & 80) ? 0xc000 : 0x0000));
  566. session->context->gps_week = (unsigned short)wn;
  567. DTOTS(&session->context->gps_tow, (double)(toc * SF_TOC));
  568. /* leap second? */
  569. GPSD_LOG(LOG_PROG, &session->context->errout,
  570. "Navcom: received packet type 0x81 (Packed Ephemeris Data)\n");
  571. GPSD_LOG(LOG_DATA, &session->context->errout,
  572. "Navcom: PRN: %u, Week: %u, TOW: %s "
  573. "SV clock bias/drift/drift rate: %#19.12E/%#19.12E/%#19.12E\n",
  574. prn,
  575. session->context->gps_week,
  576. timespec_str(&session->context->gps_tow, ts_buf, sizeof(ts_buf)),
  577. ((double)af0) * SF_AF0,
  578. ((double)af1) * SF_AF1, ((double)af2) * SF_AF2);
  579. GPSD_LOG(LOG_DATA, &session->context->errout,
  580. "Navcom: IODE (!AODE): %u Crs: %19.12e, Delta n: %19.12e, M0: %19.12e\n",
  581. iode, (double)crs * SF_CRS,
  582. (double)delta_n * SF_DELTA_N * GPS_PI,
  583. (double)m0 * SF_M0 * GPS_PI);
  584. GPSD_LOG(LOG_DATA, &session->context->errout,
  585. "Navcom: Cuc: %19.12e, Eccentricity: %19.12e, Cus: %19.12e, A^1/2: %19.12e\n",
  586. (double)cuc * SF_CUC, (double)e * SF_E, (double)cus * SF_CUS,
  587. (double)sqrt_a * SF_SQRT_A);
  588. GPSD_LOG(LOG_DATA, &session->context->errout,
  589. "Navcom: TOE: %u, Cic: %19.12e, Omega %19.12e, Cis: %19.12e\n",
  590. toe * SF_TOE, (double)cic * SF_CIC,
  591. (double)Omega0 * SF_OMEGA0 * GPS_PI, (double)cis * SF_CIS);
  592. GPSD_LOG(LOG_DATA, &session->context->errout,
  593. "Navcom: i0: %19.12e, Crc: %19.12e, omega: %19.12e, Omega dot: %19.12e\n",
  594. (double)i0 * SF_I0 * GPS_PI, (double)crc * SF_CRC,
  595. (double)omega * SF_OMEGA * GPS_PI,
  596. (double)Omegadot * SF_OMEGADOT * GPS_PI);
  597. GPSD_LOG(LOG_DATA, &session->context->errout,
  598. "Navcom: IDOT: %19.12e, Codes on L2: 0x%x, GPS Week: %u, L2 P data flag: %x\n",
  599. (double)idot * SF_IDOT * GPS_PI, cl2,
  600. week - (week % 1024) + wn, l2pd);
  601. GPSD_LOG(LOG_DATA, &session->context->errout,
  602. "Navcom: SV accuracy: 0x%x, SV health: 0x%x, TGD: %f, IODC (!AODC): %u\n",
  603. ura, svh, (double)tgd * SF_TGD, iodc);
  604. GPSD_LOG(LOG_DATA, &session->context->errout,
  605. "Navcom: Transmission time: %u\n", tow);
  606. #undef SF_TGD
  607. #undef SF_TOC
  608. #undef SF_AF2
  609. #undef SF_AF1
  610. #undef SF_AF0
  611. #undef SF_CRS
  612. #undef SF_DELTA_N
  613. #undef SF_M0
  614. #undef SF_CUC
  615. #undef SF_E
  616. #undef SF_CUS
  617. #undef SF_SQRT_A
  618. #undef SF_TOE
  619. #undef SF_CIC
  620. #undef SF_OMEGA0
  621. #undef SF_CIS
  622. #undef SF_I0
  623. #undef SF_CRC
  624. #undef SF_OMEGA
  625. #undef SF_OMEGADOT
  626. #undef SF_IDOT
  627. return 0;
  628. }
  629. /* Channel Status */
  630. static gps_mask_t handle_0x86(struct gps_device_t *session)
  631. {
  632. size_t n, i, nsu;
  633. unsigned char *buf = session->lexer.outbuffer + 3;
  634. unsigned msg_len = getleu16(buf, 1);
  635. unsigned short week = getleu16(buf, 3);
  636. uint32_t tow = getleu32(buf, 5);
  637. uint8_t eng_status = getub(buf, 9);
  638. uint16_t sol_status = getleu16(buf, 10);
  639. uint8_t sats_visible = getub(buf, 12);
  640. //uint8_t sats_tracked = getub(buf, 13);
  641. //uint8_t used_sats = getub(buf, 14);
  642. //uint8_t pdop = getub(buf, 15);
  643. timespec_t ts_tow;
  644. MSTOTS(&ts_tow, tow);
  645. /* Timestamp */
  646. session->gpsdata.skyview_time = gpsd_gpstime_resolv(session, week, ts_tow);
  647. /* Give this driver a single point of truth about DOPs */
  648. //session->gpsdata.dop.pdop = (int)pdop / 10.0;
  649. /* Satellite count */
  650. session->gpsdata.satellites_visible = (int)sats_visible;
  651. /* Fix mode */
  652. switch (sol_status & 0x05) {
  653. case 0x05:
  654. session->newdata.status = STATUS_DGPS_FIX;
  655. break;
  656. case 0x01:
  657. session->newdata.status = STATUS_FIX;
  658. break;
  659. default:
  660. session->newdata.status = STATUS_NO_FIX;
  661. }
  662. GPSD_LOG(LOG_DATA, &session->context->errout,
  663. "Navcom: engine status 0x%x almanac %s time 0x%x pos 0x%x\n",
  664. eng_status & 0x07, ((eng_status & 0x08) ? "valid" : "invalid"),
  665. eng_status & 0x30 >> 4, eng_status & 0xc0 >> 6);
  666. /* Satellite details */
  667. i = nsu = 0;
  668. if (298 < msg_len) {
  669. /* pasify coverity
  670. * msg_len = 18 + (14 * nsat)
  671. * assume 20 sats max */
  672. msg_len = 298;
  673. }
  674. for (n = 17; n < msg_len; n += 14) {
  675. uint8_t prn, ele, ca_snr, p2_snr, log_channel, hw_channel, s, stat;
  676. uint16_t azm, dgps_age;
  677. if (i >= MAXCHANNELS) {
  678. GPSD_LOG(LOG_ERROR, &session->context->errout,
  679. "Navcom: packet type 0x86: too many satellites!\n");
  680. gpsd_zero_satellites(&session->gpsdata);
  681. return 0;
  682. }
  683. prn = getub(buf, n);
  684. /*
  685. * This field is described in the Technical Reference as follows:
  686. *
  687. * Channel Tracking Status:
  688. * B0-B1: C/A tracking status
  689. * B2-B3: P1 tracking status
  690. * B4-B5: P2 tracking status
  691. * 00 Acquisition or reacquisition
  692. * 01 Code loop locked
  693. * 02 Costas loop locked
  694. * 11 Full tracking with aiding and active
  695. * multipath reduction - all data is valid
  696. * B6=1: C/A Bit sync
  697. * B7=1: C/A Frame sync
  698. *
  699. * By observation, the satellite is in use if this status is 0xff.
  700. * But errors here are not very serious, all they can affect is
  701. * the coverance-matrix calculation for error modeling.
  702. */
  703. stat = getub(buf, n + 1);
  704. log_channel = getub(buf, n + 2);
  705. ele = getub(buf, n + 5);
  706. azm = getleu16(buf, n + 6);
  707. ca_snr = getub(buf, n + 8);
  708. p2_snr = getub(buf, n + 10);
  709. dgps_age = getleu16(buf, n + 11);
  710. hw_channel = getub(buf, n + 13);
  711. s = (unsigned char)0;
  712. /* NOTE - In theory, I think one would check for hw channel number to
  713. * see if one is dealing with a GPS or other satellite, but the
  714. * channel numbers reported bear no resemblance to what the spec
  715. * says should be. So I check for the fact that if all three
  716. * values below are zero, one is not interested in this satellite */
  717. if (!(ele == 0 && azm == 0 && dgps_age == 0)) {
  718. session->gpsdata.skyview[i].PRN = (short)prn;
  719. session->gpsdata.skyview[i].elevation = (double)ele;
  720. session->gpsdata.skyview[i].azimuth = (double)azm;
  721. s = session->gpsdata.skyview[i].ss =
  722. (p2_snr ? p2_snr : ca_snr) / 4.0;
  723. session->gpsdata.skyview[i++].used = (stat == 0xff);
  724. if (stat == 0xff)
  725. nsu++;
  726. }
  727. session->gpsdata.satellites_used = (int)nsu;
  728. GPSD_LOG(LOG_DATA, &session->context->errout,
  729. "Navcom: prn = %3u, ele = %02u, azm = %03u, snr = %d (%s), "
  730. "dgps age = %.1fs, log ch = %d, hw ch = 0x%02x\n",
  731. prn, ele, azm, s, (p2_snr ? "P2" : "C/A"),
  732. (double)dgps_age * 0.1, log_channel & 0x3f, hw_channel);
  733. GPSD_LOG(LOG_DATA, &session->context->errout,
  734. "Navcom: sol. valid = %c, clock = %s, pos. = %s, "
  735. "height = %s, err. code = 0x%x\n",
  736. ((sol_status & 0x01) ? 'Y' : 'N'),
  737. ((sol_status & 0x02) ? "stable" : "unstable"),
  738. ((sol_status & 0x04) ? "dgps" : "unaided"),
  739. ((sol_status & 0x08) ? "solved" : "constrained"),
  740. ((sol_status & 0x01) ? 0x00 : sol_status & 0x0f00 >> 8));
  741. }
  742. GPSD_LOG(LOG_DATA, &session->context->errout,
  743. "CS 0x86: visible=%d, used=%d, mask={SATELLITE|STATUS}\n",
  744. session->gpsdata.satellites_visible,
  745. session->gpsdata.satellites_used);
  746. return SATELLITE_SET | STATUS_SET;
  747. }
  748. /* Raw Meas. Data Block
  749. * Size 4 + 8 + (16 * numSat) = 524
  750. */
  751. static gps_mask_t handle_0xb0(struct gps_device_t *session)
  752. {
  753. char ts_buf[TIMESPEC_LEN];
  754. /* L1 wavelength (299792458m/s / 1575420000Hz)
  755. * from their Technical reference Manual */
  756. #define LAMBDA_L1 (299792458.0 / 1575420000.0)
  757. unsigned n;
  758. unsigned char *buf = session->lexer.outbuffer + 3;
  759. unsigned msg_len = getleu16(buf, 1);
  760. uint16_t week = getleu16(buf, 3);
  761. uint32_t tow = getleu32(buf, 5);
  762. uint8_t tm_slew_acc = getub(buf, 9);
  763. uint8_t status = getub(buf, 10);
  764. session->context->gps_week = (unsigned short)week;
  765. MSTOTS(&session->context->gps_tow, tow);
  766. GPSD_LOG(LOG_PROG, &session->context->errout,
  767. "Navcom: received packet type 0xb0 (Raw Meas. Data Block)\n");
  768. GPSD_LOG(LOG_DATA, &session->context->errout,
  769. "Navcom: week = %u, tow = %s "
  770. "time slew accumulator = %u (1/1023mS), status = 0x%02x "
  771. "(%sclock %s - %u blocks follow)\n",
  772. session->context->gps_week,
  773. timespec_str(&session->context->gps_tow, ts_buf, sizeof(ts_buf)),
  774. tm_slew_acc, status,
  775. ((status & 0x80) ? "channel time set - " : ""),
  776. ((status & 0x40) ? "stable" : "not stable"), status & 0x0f);
  777. if (530 < msg_len) {
  778. GPSD_LOG(LOG_PROG, &session->context->errout,
  779. "Navcom: received packet type 0xb0, length %u too long\n",
  780. msg_len);
  781. return 0;
  782. }
  783. for (n = 11; n < (msg_len - 1); n += 16) {
  784. uint8_t sv_status = getub(buf, n);
  785. uint8_t ch_status = getub(buf, n + 1);
  786. uint32_t ca_pseudorange = getleu32(buf, n + 2);
  787. /* integer division by 16 is a sign-preserving right shift of 4 bits */
  788. int32_t l1_phase = getles3224(buf, n + 6) / 16;
  789. uint8_t l1_slips = (uint8_t) (getles3224(buf, n + 6) & 0x0f);
  790. int16_t p1_ca_pseudorange = getles16(buf, n + 9);
  791. int16_t p2_ca_pseudorange = getles16(buf, n + 11);
  792. int32_t l2_phase = getles3224(buf, n + 13) / 16;
  793. uint8_t l2_slips = (uint8_t) (getles3224(buf, n + 13) & 0x0f);
  794. double c1 =
  795. ((sv_status & 0x80) ? (double)ca_pseudorange / 16.0 *
  796. LAMBDA_L1 : NAN);
  797. double l1 =
  798. ((sv_status & 0x80) ? (double)ca_pseudorange / 16.0 +
  799. (double)l1_phase / 256.0 : NAN);
  800. double l2 =
  801. ((sv_status & 0x20)
  802. ? ((double)ca_pseudorange / 16.0 +
  803. (double)p2_ca_pseudorange / 16.0) * (120.0 / 154.0)
  804. + (double)l2_phase / 256.0 : NAN);
  805. double p1 =
  806. ((sv_status & 0x40) ? c1 +
  807. (double)p1_ca_pseudorange / 16.0 * LAMBDA_L1 : NAN);
  808. double p2 =
  809. ((sv_status & 0x20) ? c1 +
  810. (double)p2_ca_pseudorange / 16.0 * LAMBDA_L1 : NAN);
  811. GPSD_LOG(LOG_SPIN, &session->context->errout,
  812. "Navcom: >> sv status = 0x%02x (PRN %u - C/A & L1 %s - P1 %s - P2 & L2 %s)\n",
  813. sv_status, (sv_status & 0x1f),
  814. ((sv_status & 0x80) ? "valid" : "invalid"),
  815. ((sv_status & 0x40) ? "valid" : "invalid"),
  816. ((sv_status & 0x20) ? "valid" : "invalid"));
  817. GPSD_LOG(LOG_SPIN, &session->context->errout,
  818. "Navcom: >>> ch status = 0x%02x "
  819. "(Logical channel: %u - CA C/No: %u dBHz) "
  820. "sL1: %u, sL2: %u\n", ch_status, ch_status & 0x0f,
  821. ((ch_status & 0xf0) >> 4) + 35, l1_slips, l2_slips);
  822. GPSD_LOG(LOG_SPIN, &session->context->errout,
  823. "Navcom: >>> C1: %14.3f, L1: %14.3f, L2: %14.3f, P1: %14.3f, P2: %14.3f\n",
  824. c1, l1, l2, p1, p2);
  825. }
  826. #undef LAMBDA_L1
  827. return 0; /* Raw measurements not yet implemented in gpsd */
  828. }
  829. /* Pseudorange Noise Statistics */
  830. static gps_mask_t handle_0xb5(struct gps_device_t *session)
  831. {
  832. if (sizeof(double) == 8) {
  833. gps_mask_t mask = TIME_SET;
  834. char *buf = (char *)session->lexer.outbuffer + 3;
  835. uint16_t week = getleu16(buf, 3);
  836. uint32_t tow = getleu32(buf, 5);
  837. timespec_t ts_tow;
  838. #ifdef __UNUSED__
  839. double rms = getled64(buf, 9);
  840. /* Reason why it's unused is these figures do not agree
  841. * with those obtained from the PVT report (handle_0xb1).
  842. * The figures from 0xb1 do agree with the values reported
  843. * by Navcom's PC utility */
  844. /* let gpsd_error_model() handle this */
  845. //double ellips_maj = getled64(buf, 17);
  846. //double ellips_min = getled64(buf, 25);
  847. //double ellips_azm = getled64(buf, 33);
  848. double lat_sd = getled64(buf, 41);
  849. double lon_sd = getled64(buf, 49);
  850. double alt_sd = getled64(buf, 57);
  851. double hrms = sqrt(pow(lat_sd, 2) + pow(lon_sd, 2));
  852. /* Navcom doc unclear, this is likely sep? */
  853. session->newdata.sep = rms * 1.96;
  854. session->newdata.eph = hrms * 1.96;
  855. session->newdata.epv = alt_sd * 1.96;
  856. mask |= HERR_SET;
  857. #endif /* __UNUSED__ */
  858. MSTOTS(&ts_tow, tow);
  859. session->newdata.time = gpsd_gpstime_resolv(session,
  860. (unsigned short)week,
  861. ts_tow);
  862. GPSD_LOG(LOG_PROG, &session->context->errout,
  863. "Navcom: received packet type 0xb5 (Pseudorange Noise Statistics)\n");
  864. GPSD_LOG(LOG_DATA, &session->context->errout,
  865. "Navcom: sep = %f\n", session->newdata.sep);
  866. return mask;
  867. } else {
  868. /* Ignore this message block */
  869. if (!session->driver.navcom.warned) {
  870. GPSD_LOG(LOG_WARN, &session->context->errout,
  871. "Navcom: received packet type 0xb5 (Pseudorange "
  872. "Noise Statistics) ignored "
  873. " - sizeof(double) == 64 bits required\n");
  874. session->driver.navcom.warned = true;
  875. }
  876. return 0; /* Block ignored - wrong sizeof(double) */
  877. }
  878. }
  879. /* LBM DSP Status Block */
  880. static gps_mask_t handle_0xd3(struct gps_device_t *session UNUSED)
  881. {
  882. /* This block contains status information about the
  883. * unit's L-band (Inmarsat) module. There is nothing
  884. * interesting in it for our purposes so we do not deal
  885. * with it. This callback is purely to a) stop
  886. * "unrecognised packet" messages appearing in the log
  887. * and b) explain what it is for the curious */
  888. return 0; /* Nothing updated */
  889. }
  890. /* Identification Block */
  891. static gps_mask_t handle_0xae(struct gps_device_t *session)
  892. {
  893. char *engconfstr, *asicstr;
  894. unsigned char *buf = session->lexer.outbuffer + 3;
  895. size_t msg_len = (size_t) getleu16(buf, 1);
  896. uint8_t engconf = getub(buf, 3);
  897. uint8_t asic = getub(buf, 4);
  898. uint8_t swvermaj = getub(buf, 5);
  899. uint8_t swvermin = getub(buf, 6);
  900. uint16_t dcser = getleu16(buf, 7);
  901. uint8_t dcclass = getub(buf, 9);
  902. uint16_t rfcser = getleu16(buf, 10);
  903. uint8_t rfcclass = getub(buf, 12);
  904. uint8_t softtm[17] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
  905. uint8_t bootstr[17] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
  906. uint8_t ioptm[17] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
  907. uint8_t iopvermaj = (uint8_t) 0x00;
  908. uint8_t iopvermin = (uint8_t) 0x00;
  909. uint8_t picver = (uint8_t) 0x00;
  910. uint8_t slsbn = (uint8_t) 0x00;
  911. uint8_t iopsbn = (uint8_t) 0x00;
  912. memcpy(softtm, &buf[13], 16);
  913. memcpy(bootstr, &buf[29], 16);
  914. if (msg_len == 0x0037) { /* No IOP */
  915. slsbn = getub(buf, 53);
  916. } else { /* IOP Present */
  917. iopvermaj = getub(buf, 53);
  918. iopvermin = getub(buf, 54);
  919. memcpy(ioptm, &buf[55], 16);
  920. picver = getub(buf, 71);
  921. slsbn = getub(buf, 72);
  922. iopsbn = getub(buf, 73);
  923. }
  924. switch (engconf) {
  925. case 0x00:
  926. engconfstr = "Unknown/Undefined";
  927. break;
  928. case 0x01:
  929. engconfstr = "NCT 2000 S";
  930. break;
  931. case 0x02:
  932. engconfstr = "NCT 2000 D";
  933. break;
  934. case 0x03:
  935. engconfstr = "Startfire Single";
  936. break;
  937. case 0x04:
  938. engconfstr = "Starfire Dual";
  939. break;
  940. case 0x05:
  941. engconfstr = "Pole Mount RTK (Internal Radio)";
  942. break;
  943. case 0x06:
  944. engconfstr = "Pole Mount GIS (LBM)";
  945. break;
  946. case 0x07:
  947. engconfstr = "Black Box RTK (Internal Radio)";
  948. break;
  949. case 0x08:
  950. engconfstr = "Black Box GIS (LBM)";
  951. break;
  952. case 0x80:
  953. engconfstr = "R100";
  954. break;
  955. case 0x81:
  956. engconfstr = "R200";
  957. break;
  958. case 0x82:
  959. engconfstr = "R210";
  960. break;
  961. case 0x83:
  962. engconfstr = "R300";
  963. break;
  964. case 0x84:
  965. engconfstr = "R310";
  966. break;
  967. default:
  968. engconfstr = "?";
  969. }
  970. switch (asic) {
  971. case 0x01:
  972. asicstr = "A-ASIC";
  973. break;
  974. case 0x02:
  975. asicstr = "B-ASIC";
  976. break;
  977. case 0x03:
  978. asicstr = "C-ASIC";
  979. break;
  980. case 0x04:
  981. asicstr = "M-ASIC";
  982. break;
  983. default:
  984. asicstr = "?";
  985. }
  986. GPSD_LOG(LOG_PROG, &session->context->errout,
  987. "Navcom: received packet type 0xae (Identification Block)\n");
  988. if (msg_len == 0x0037) {
  989. GPSD_LOG(LOG_INF, &session->context->errout, "Navcom: ID Data: "
  990. "%s %s Ver. %u.%u.%u, DC S/N: %u.%u, RF S/N: %u.%u, "
  991. "Build ID: %s, Boot software: %s\n",
  992. engconfstr, asicstr, swvermaj, swvermin, slsbn, dcser,
  993. dcclass, rfcser, rfcclass, softtm, bootstr);
  994. } else {
  995. GPSD_LOG(LOG_INF, &session->context->errout, "Navcom: ID Data: "
  996. "%s %s Ver. %u.%u.%u, DC S/N: %u.%u, RF S/N: %u.%u, "
  997. "Build ID: %s, Boot software: %s, "
  998. "IOP Ver.: %u.%u.%u, PIC: %u, IOP Build ID: %s\n",
  999. engconfstr, asicstr, swvermaj, swvermin, slsbn, dcser,
  1000. dcclass, rfcser, rfcclass, softtm, bootstr, iopvermaj,
  1001. iopvermin, iopsbn, picver, ioptm);
  1002. }
  1003. (void)snprintf(session->subtype, sizeof(session->subtype),
  1004. "%s %s Ver. %u.%u.%u S/N %u.%u %u.%u",
  1005. engconfstr, asicstr, swvermaj, swvermin, slsbn, dcser,
  1006. dcclass, rfcser, rfcclass);
  1007. return DEVICEID_SET;
  1008. }
  1009. /* Clock Drift and Offset */
  1010. static gps_mask_t handle_0xef(struct gps_device_t *session)
  1011. {
  1012. unsigned char *buf = session->lexer.outbuffer + 3;
  1013. //uint16_t week = getleu16(buf, 3);
  1014. //uint32_t tow = getleu32(buf, 5);
  1015. int8_t osc_temp = getsb(buf, 9);
  1016. uint8_t nav_status = getub(buf, 10);
  1017. double nav_clock_offset;
  1018. float nav_clock_drift;
  1019. float osc_filter_drift_est;
  1020. char ts_buf[TIMESPEC_LEN];
  1021. int32_t time_slew = (int32_t) getles32(buf, 27);
  1022. if (sizeof(double) == 8) {
  1023. nav_clock_offset = getled64((char *)buf, 11);
  1024. } else {
  1025. nav_clock_offset = NAN;
  1026. }
  1027. if (sizeof(float) == 4) {
  1028. nav_clock_drift = getlef32((char *)buf, 19);
  1029. osc_filter_drift_est = getlef32((char *)buf, 23);
  1030. } else {
  1031. nav_clock_drift = NAN;
  1032. osc_filter_drift_est = NAN;
  1033. }
  1034. GPSD_LOG(LOG_DATA, &session->context->errout,
  1035. "Navcom: oscillator temp. = %d, nav. status = 0x%02x, "
  1036. "nav. clock offset = %f, nav. clock drift = %f, "
  1037. "osc. filter drift est. = %f, acc.time slew value = %d\n",
  1038. osc_temp, nav_status, nav_clock_offset, nav_clock_drift,
  1039. osc_filter_drift_est, time_slew);
  1040. GPSD_LOG(LOG_DATA, &session->context->errout,
  1041. "CDO 0xef: time=%s mask={TIME}\n",
  1042. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)));
  1043. return 0;
  1044. }
  1045. gps_mask_t navcom_parse(struct gps_device_t * session, unsigned char *buf,
  1046. size_t len)
  1047. {
  1048. unsigned char cmd_id;
  1049. unsigned int msg_len;
  1050. if (len == 0)
  1051. return 0;
  1052. cmd_id = (unsigned char)getub(buf, 3);
  1053. //payload = &buf[6];
  1054. msg_len = (unsigned int) getleu16(buf, 4);
  1055. GPSD_LOG(LOG_RAW, &session->context->errout,
  1056. "Navcom: packet type 0x%02x\n", cmd_id);
  1057. session->cycle_end_reliable = true;
  1058. switch (cmd_id) {
  1059. case 0x06:
  1060. return handle_0x06(session);
  1061. case 0x15:
  1062. return handle_0x15(session);
  1063. case 0x81:
  1064. return handle_0x81(session);
  1065. case 0x83:
  1066. return handle_0x83(session);
  1067. case 0x86:
  1068. return handle_0x86(session);
  1069. case 0xae:
  1070. return handle_0xae(session);
  1071. case 0xb0:
  1072. return handle_0xb0(session);
  1073. case 0xb1:
  1074. return handle_0xb1(session) | (CLEAR_IS | REPORT_IS);
  1075. case 0xb5:
  1076. return handle_0xb5(session);
  1077. case 0xd3:
  1078. return handle_0xd3(session);
  1079. case 0xef:
  1080. return handle_0xef(session);
  1081. default:
  1082. GPSD_LOG(LOG_PROG, &session->context->errout,
  1083. "Navcom: received packet type 0x%02x, length %d - "
  1084. "unknown or unimplemented\n",
  1085. cmd_id, msg_len);
  1086. return 0;
  1087. }
  1088. }
  1089. static gps_mask_t navcom_parse_input(struct gps_device_t *session)
  1090. {
  1091. if (session->lexer.type == NAVCOM_PACKET) {
  1092. return navcom_parse(session, session->lexer.outbuffer,
  1093. session->lexer.outbuflen);
  1094. #ifdef NMEA0183_ENABLE
  1095. } else if (session->lexer.type == NMEA_PACKET) {
  1096. return nmea_parse((char *)session->lexer.outbuffer, session);;
  1097. #endif /* NMEA0183_ENABLE */
  1098. } else
  1099. return 0;
  1100. }
  1101. static ssize_t navcom_control_send(struct gps_device_t *session,
  1102. char *buf, size_t len)
  1103. {
  1104. putbyte(session->msgbuf, 0, 0x02);
  1105. putbyte(session->msgbuf, 1, 0x99);
  1106. putbyte(session->msgbuf, 2, 0x66);
  1107. putbyte(session->msgbuf, 3, buf[0]); /* Cmd ID */
  1108. putle16(session->msgbuf, 4, len + 4); /* Length */
  1109. memcpy(session->msgbuf, buf + 6, len - 1);
  1110. putbyte(session->msgbuf, 6 + len,
  1111. checksum((unsigned char *)session->msgbuf + 3, len + 5));
  1112. putbyte(session->msgbuf, 7 + len, 0x03);
  1113. session->msgbuflen = len + 9;
  1114. return gpsd_write(session, session->msgbuf, session->msgbuflen);
  1115. }
  1116. static bool navcom_speed(struct gps_device_t *session,
  1117. speed_t speed, char parity, int stopbits)
  1118. {
  1119. /* parity and stopbit switching aren't implemented */
  1120. if (parity != session->gpsdata.dev.parity
  1121. || stopbits != (int)session->gpsdata.dev.parity) {
  1122. return false;
  1123. } else {
  1124. uint8_t port, port_selection;
  1125. uint8_t baud;
  1126. if (session->driver.navcom.physical_port == (uint8_t) 0xFF) {
  1127. /* We still don't know which port we're connected to */
  1128. return false;
  1129. }
  1130. switch (speed) {
  1131. /* NOTE - The spec says that certain baud combinations
  1132. * on ports A and B are not allowed, those are
  1133. * 1200/115200, 2400/57600, and 2400/115200.
  1134. * To try and minimise the possibility of those
  1135. * occurring, we do not allow baud rates below
  1136. * 4800. We could also disallow 57600 and 115200
  1137. * to totally prevent this, but I do not consider
  1138. * that reasonable. Finding which baud speed the
  1139. * other port is set at would also be too much
  1140. * trouble, so we do not do it. */
  1141. case 4800:
  1142. baud = 0x04;
  1143. break;
  1144. case 9600:
  1145. baud = 0x06;
  1146. break;
  1147. case 19200:
  1148. baud = 0x08;
  1149. break;
  1150. case 38400:
  1151. baud = 0x0a;
  1152. break;
  1153. case 57600:
  1154. baud = 0x0c;
  1155. break;
  1156. case 115200:
  1157. baud = 0x0e;
  1158. break;
  1159. default:
  1160. /* Unsupported speed */
  1161. return false;
  1162. }
  1163. /* Proceed to construct our message */
  1164. port = session->driver.navcom.physical_port;
  1165. port_selection = (port ? port : (uint8_t) 0xff) | baud;
  1166. /* Send it off */
  1167. navcom_cmd_0x11(session, port_selection);
  1168. /* And cheekily return true, even though we have
  1169. * no way to know if the speed change succeeded
  1170. * until and if we receive an ACK (message 0x06),
  1171. * which will be at the new baud speed if the
  1172. * command was successful. Bottom line, the client
  1173. * should requery gpsd to see if the new speed is
  1174. * different than the old one */
  1175. return true;
  1176. }
  1177. }
  1178. /* this is everything we export */
  1179. /* *INDENT-OFF* */
  1180. const struct gps_type_t driver_navcom =
  1181. {
  1182. .type_name = "Navcom NCT", /* full name of type */
  1183. .packet_type = NAVCOM_PACKET, /* lexer packet type */
  1184. .flags = DRIVER_STICKY, /* remember this */
  1185. .trigger = NULL, /* none */
  1186. .channels = NAVCOM_CHANNELS, // 12 L1 + 12 L2 + 2 Inmarsat L-Band
  1187. .probe_detect = NULL, /* no probe */
  1188. .get_packet = generic_get, /* use generic one */
  1189. .parse_packet = navcom_parse_input, /* parse message packets */
  1190. .rtcm_writer = gpsd_write, /* send RTCM data straight */
  1191. .init_query = NULL, /* non-perturbing query */
  1192. .event_hook = navcom_event_hook, /* lifetime event handler */
  1193. .speed_switcher = navcom_speed, /* we do change baud rates */
  1194. .mode_switcher = NULL, // there is not a mode switcher
  1195. .rate_switcher = NULL, /* no sample-rate switcher */
  1196. .min_cycle.tv_sec = 1, /* not relevant, no rate switch */
  1197. .min_cycle.tv_nsec = 0, /* not relevant, no rate switch */
  1198. .control_send = navcom_control_send, // how to send a control string
  1199. .time_offset = NULL, /* no method for NTP fudge factor */
  1200. };
  1201. /* *INDENT-ON* */
  1202. #endif /* defined(NAVCOM_ENABLE) */
  1203. // vim: set expandtab shiftwidth=4