subframe.c 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. /* subframe.c -- interpret satellite subframe data.
  2. *
  3. * This file is Copyright 2010 by the GPSD project
  4. * SPDX-License-Identifier: BSD-2-clause
  5. */
  6. #include "gpsd_config.h" /* must be before all includes */
  7. #include <math.h>
  8. #include "gpsd.h"
  9. /* convert unsigned to signed */
  10. #define uint2int( u, bit) ( (u & (1<<(bit-1))) ? u - (1<<bit) : u)
  11. gps_mask_t gpsd_interpret_subframe_raw(struct gps_device_t *session,
  12. unsigned int tSVID, uint32_t words[])
  13. {
  14. unsigned int i;
  15. uint8_t preamble;
  16. if (session->subframe_count++ == 0) {
  17. speed_t speed = gpsd_get_speed(session);
  18. if (speed < 38400)
  19. GPSD_LOG(LOG_WARN, &session->context->errout,
  20. "speed less than 38,400 may cause data lag and "
  21. "loss of functionality\n");
  22. }
  23. /*
  24. * This function assumes an array of 10 ints, each of which carries
  25. * a raw 30-bit GPS word use your favorite search engine to find the
  26. * latest version of the specification: IS-GPS-200.
  27. *
  28. * Each raw 30-bit word is made of 24 data bits and 6 parity bits. The
  29. * raw word and transport word are emitted from the GPS MSB-first and
  30. * right justified. In other words, masking the raw word against 0x3f
  31. * will return just the parity bits. Masking with 0x3fffffff and shifting
  32. * 6 bits to the right returns just the 24 data bits. The top two bits
  33. * (b31 and b30) are undefined; chipset designers may store copies of
  34. * the bits D29* and D30* here to aid parity checking.
  35. *
  36. * Since bits D29* and D30* are not available in word 0, it is tested for
  37. * a known preamble to help check its validity and determine whether the
  38. * word is inverted.
  39. *
  40. */
  41. GPSD_LOG(LOG_DATA, &session->context->errout,
  42. "50B: gpsd_interpret_subframe_raw: "
  43. "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
  44. words[0], words[1], words[2], words[3], words[4],
  45. words[5], words[6], words[7], words[8], words[9]);
  46. preamble = (uint8_t)((words[0] >> 22) & 0xFF);
  47. if (preamble == 0x8b) { /* preamble is inverted */
  48. words[0] ^= 0x3fffffc0; /* invert */
  49. } else if (preamble != 0x74) {
  50. /* strangely this is very common, so don't log it */
  51. GPSD_LOG(LOG_DATA, &session->context->errout,
  52. "50B: gpsd_interpret_subframe_raw: bad preamble 0x%x\n",
  53. preamble);
  54. return 0;
  55. }
  56. words[0] = (words[0] >> 6) & 0xffffff;
  57. for (i = 1; i < 10; i++) {
  58. int invert;
  59. uint32_t parity;
  60. /* D30* says invert */
  61. invert = (words[i] & 0x40000000) ? 1 : 0;
  62. /* inverted data, invert it back */
  63. if (invert) {
  64. words[i] ^= 0x3fffffc0;
  65. }
  66. parity = (uint32_t)isgps_parity((isgps30bits_t)words[i]);
  67. if (parity != (words[i] & 0x3f)) {
  68. GPSD_LOG(LOG_DATA, &session->context->errout,
  69. "50B: gpsd_interpret_subframe_raw parity fail "
  70. "words[%d] 0x%x != 0x%x\n",
  71. i, parity, (words[i] & 0x1));
  72. return 0;
  73. }
  74. words[i] = (words[i] >> 6) & 0xffffff;
  75. }
  76. return gpsd_interpret_subframe(session, tSVID, words);
  77. }
  78. /* you can find up to date almanac data for comparison here:
  79. * https://gps.afspc.af.mil/gps/Current/current.alm
  80. */
  81. static void subframe_almanac(const struct gpsd_errout_t *errout,
  82. uint8_t tSVID, uint32_t words[],
  83. uint8_t subframe, uint8_t sv,
  84. uint8_t data_id,
  85. struct almanac_t *almp)
  86. {
  87. almp->sv = sv; /* ignore the 0 sv problem for now */
  88. almp->e = ( words[2] & 0x00FFFF);
  89. almp->d_eccentricity = pow(2.0,-21) * almp->e;
  90. /* careful, each SV can have more than 2 toa's active at the same time
  91. * you can not just store one or two almanacs for each sat */
  92. almp->toa = ((words[3] >> 16) & 0x0000FF);
  93. almp->l_toa = almp->toa << 12;
  94. almp->deltai = ( words[3] & 0x00FFFF);
  95. almp->d_deltai = pow(2.0, -19) * almp->deltai;
  96. almp->Omegad = ((words[4] >> 8) & 0x00FFFF);
  97. almp->d_Omegad = pow(2.0, -38) * almp->Omegad;
  98. almp->svh = ( words[4] & 0x0000FF);
  99. almp->sqrtA = ( words[5] & 0xFFFFFF);
  100. almp->d_sqrtA = pow(2.0,-11) * almp->sqrtA;
  101. almp->Omega0 = ( words[6] & 0xFFFFFF);
  102. almp->Omega0 = uint2int(almp->Omega0, 24);
  103. almp->d_Omega0 = pow(2.0, -23) * almp->Omega0;
  104. almp->omega = ( words[7] & 0xFFFFFF);
  105. almp->omega = uint2int(almp->omega, 24);
  106. almp->d_omega = pow(2.0, -23) * almp->omega;
  107. almp->M0 = ( words[8] & 0x00FFFFFF);
  108. almp->M0 = uint2int(almp->M0, 24);
  109. /* if you want radians, multiply by GPS_PI, but we do semi-circles
  110. * to match IS-GPS-200E */
  111. almp->d_M0 = pow(2.0,-23) * almp->M0;
  112. almp->af1 = ((words[9] >> 5) & 0x0007FF);
  113. almp->af1 = (short)uint2int(almp->af1, 11);
  114. almp->d_af1 = pow(2.0,-38) * almp->af1;
  115. almp->af0 = ((words[9] >> 16) & 0x0000FF);
  116. almp->af0 <<= 3;
  117. almp->af0 |= ((words[9] >> 2) & 0x000007);
  118. almp->af0 = (short)uint2int(almp->af0, 11);
  119. almp->d_af0 = pow(2.0,-20) * almp->af0;
  120. GPSD_LOG(LOG_PROG, errout,
  121. "50B: SF:%d SV:%2u TSV:%2u data_id %d e:%g toa:%lu "
  122. "deltai:%.10e Omegad:%.5e svh:%u sqrtA:%.10g Omega0:%.10e "
  123. "omega:%.10e M0:%.11e af0:%.5e af1:%.5e\n",
  124. subframe, almp->sv, tSVID, data_id,
  125. almp->d_eccentricity,
  126. almp->l_toa,
  127. almp->d_deltai,
  128. almp->d_Omegad,
  129. almp->svh,
  130. almp->d_sqrtA,
  131. almp->d_Omega0,
  132. almp->d_omega,
  133. almp->d_M0,
  134. almp->d_af0,
  135. almp->d_af1);
  136. }
  137. gps_mask_t gpsd_interpret_subframe(struct gps_device_t *session,
  138. unsigned int tSVID, uint32_t words[])
  139. {
  140. /*
  141. * Heavy black magic begins here!
  142. *
  143. * A description of how to decode these bits is at
  144. * <http://home-2.worldonline.nl/~samsvl/nav2eu.htm>
  145. *
  146. * We're mostly looking for subframe 4 page 18 word 9, the leap second
  147. * correction. This functions assumes an array of words without parity
  148. * or inversion (inverted word 0 is OK). It may be called directly by a
  149. * driver if the chipset emits acceptable data.
  150. *
  151. * To date this code has been tested on iTrax, SiRF and ublox.
  152. */
  153. /* FIXME!! I really doubt this is Big Endian compatible */
  154. uint8_t preamble;
  155. struct subframe_t *subp = &session->gpsdata.subframe;
  156. GPSD_LOG(LOG_DATA, &session->context->errout,
  157. "50B: gpsd_interpret_subframe: (%d) "
  158. "%06x %06x %06x %06x %06x %06x %06x %06x %06x %06x\n",
  159. tSVID, words[0], words[1], words[2], words[3], words[4],
  160. words[5], words[6], words[7], words[8], words[9]);
  161. preamble = (uint8_t)((words[0] >> 16) & 0x0FF);
  162. if (preamble == 0x8b) {
  163. /* somehow missed an inversion */
  164. preamble ^= 0xff;
  165. words[0] ^= 0xffffff;
  166. }
  167. if (preamble != 0x74) {
  168. GPSD_LOG(LOG_WARN, &session->context->errout,
  169. "50B: gpsd_interpret_subframe bad preamble: "
  170. "0x%x header 0x%x\n",
  171. preamble, words[0]);
  172. return 0;
  173. }
  174. subp->integrity = (bool)((words[0] >> 1) & 0x01);
  175. /* The subframe ID is in the Hand Over Word (page 80) */
  176. subp->TOW17 = ((words[1] >> 7) & 0x01FFFF);
  177. subp->l_TOW17 = (long)(subp->TOW17 * 6);
  178. subp->tSVID = (uint8_t)tSVID;
  179. subp->subframe_num = ((words[1] >> 2) & 0x07);
  180. subp->alert = (bool)((words[1] >> 6) & 0x01);
  181. subp->antispoof = (bool)((words[1] >> 6) & 0x01);
  182. GPSD_LOG(LOG_PROG, &session->context->errout,
  183. "50B: SF:%d SV:%2u TOW17:%7lu Alert:%u AS:%u IF:%d\n",
  184. subp->subframe_num, subp->tSVID, subp->l_TOW17,
  185. (unsigned)subp->alert, (unsigned)subp->antispoof,
  186. (unsigned)subp->integrity);
  187. /*
  188. * Consult the latest revision of IS-GPS-200 for the mapping
  189. * between magic SVIDs and pages.
  190. */
  191. subp->pageid = (words[2] >> 16) & 0x00003F; /* only in frames 4 & 5 */
  192. subp->data_id = (words[2] >> 22) & 0x3; /* only in frames 4 & 5 */
  193. subp->is_almanac = 0;
  194. switch (subp->subframe_num) {
  195. case 1:
  196. /* subframe 1: clock parameters for transmitting SV */
  197. /* get Week Number (WN) from subframe 1 */
  198. /*
  199. * This only extracts 10 bits of GPS week.
  200. * 13 bits are available in the extension CNAV message,
  201. * which we don't decode yet because we don't know
  202. * of any receiver that reports it.
  203. */
  204. session->context->gps_week =
  205. (unsigned short)((words[2] >> 14) & 0x03ff);
  206. subp->sub1.WN = (uint16_t)session->context->gps_week;
  207. subp->sub1.l2 = (uint8_t)((words[2] >> 12) & 0x000003); /* L2 Code */
  208. /* URA Index */
  209. subp->sub1.ura = (unsigned int)((words[2] >> 8) & 0x00000F);
  210. /* SV health */
  211. subp->sub1.hlth = (unsigned int)((words[2] >> 2) & 0x00003F);
  212. subp->sub1.IODC = (words[2] & 0x000003); /* IODC 2 MSB */
  213. subp->sub1.l2p = ((words[3] >> 23) & 0x000001); /* L2 P flag */
  214. subp->sub1.Tgd = (int8_t)( words[6] & 0x0000FF);
  215. subp->sub1.d_Tgd = pow(2.0, -31) * (int)subp->sub1.Tgd;
  216. subp->sub1.toc = ( words[7] & 0x00FFFF);
  217. subp->sub1.l_toc = (long)subp->sub1.toc << 4;
  218. subp->sub1.af2 = (int8_t)((words[8] >> 16) & 0x0FF);
  219. subp->sub1.d_af2 = pow(2.0, -55) * (int)subp->sub1.af2;
  220. subp->sub1.af1 = (int16_t)( words[8] & 0x00FFFF);
  221. subp->sub1.d_af1 = pow(2.0, -43) * subp->sub1.af1;
  222. subp->sub1.af0 = (int32_t)((words[9] >> 2) & 0x03FFFFF);
  223. subp->sub1.af0 = uint2int(subp->sub1.af0, 22);
  224. subp->sub1.d_af0 = pow(2.0, -31) * subp->sub1.af0;
  225. subp->sub1.IODC <<= 8;
  226. subp->sub1.IODC |= ((words[7] >> 16) & 0x00FF);
  227. GPSD_LOG(LOG_PROG, &session->context->errout,
  228. "50B: SF:1 SV:%2u WN:%4u IODC:%4u"
  229. " L2:%u ura:%u hlth:%u L2P:%u Tgd:%g toc:%lu af2:%.4g"
  230. " af1:%.6e af0:%.7e\n",
  231. subp->tSVID,
  232. subp->sub1.WN,
  233. subp->sub1.IODC,
  234. subp->sub1.l2,
  235. subp->sub1.ura,
  236. subp->sub1.hlth,
  237. subp->sub1.l2p,
  238. subp->sub1.d_Tgd,
  239. subp->sub1.l_toc,
  240. subp->sub1.d_af2,
  241. subp->sub1.d_af1,
  242. subp->sub1.d_af0);
  243. break;
  244. case 2:
  245. /* subframe 2: ephemeris for transmitting SV */
  246. subp->sub2.IODE = ((words[2] >> 16) & 0x00FF);
  247. subp->sub2.Crs = (int16_t)( words[2] & 0x00FFFF);
  248. subp->sub2.d_Crs = pow(2.0,-5) * subp->sub2.Crs;
  249. subp->sub2.deltan = (int16_t)((words[3] >> 8) & 0x00FFFF);
  250. subp->sub2.d_deltan = pow(2.0,-43) * subp->sub2.deltan;
  251. subp->sub2.M0 = (int32_t)( words[3] & 0x0000FF);
  252. subp->sub2.M0 <<= 24;
  253. subp->sub2.M0 |= ( words[4] & 0x00FFFFFF);
  254. subp->sub2.d_M0 = pow(2.0,-31) * subp->sub2.M0 * GPS_PI;
  255. subp->sub2.Cuc = (int16_t)((words[5] >> 8) & 0x00FFFF);
  256. subp->sub2.d_Cuc = pow(2.0,-29) * subp->sub2.Cuc;
  257. subp->sub2.e = ( words[5] & 0x0000FF);
  258. subp->sub2.e <<= 24;
  259. subp->sub2.e |= ( words[6] & 0x00FFFFFF);
  260. subp->sub2.d_eccentricity = pow(2.0,-33) * subp->sub2.e;
  261. subp->sub2.Cus = (int16_t)((words[7] >> 8) & 0x00FFFF);
  262. subp->sub2.d_Cus = pow(2.0,-29) * subp->sub2.Cus;
  263. subp->sub2.sqrtA = ( words[7] & 0x0000FF);
  264. subp->sub2.sqrtA <<= 24;
  265. subp->sub2.sqrtA |= ( words[8] & 0x00FFFFFF);
  266. subp->sub2.d_sqrtA = pow(2.0, -19) * subp->sub2.sqrtA;
  267. subp->sub2.toe = ((words[9] >> 8) & 0x00FFFF);
  268. subp->sub2.l_toe = (long)(subp->sub2.toe << 4);
  269. subp->sub2.fit = ((words[9] >> 7) & 0x000001);
  270. subp->sub2.AODO = ((words[9] >> 2) & 0x00001F);
  271. subp->sub2.u_AODO = subp->sub2.AODO * 900;
  272. GPSD_LOG(LOG_PROG, &session->context->errout,
  273. "50B: SF:2 SV:%2u IODE:%3u Crs:%.6e deltan:%.6e "
  274. "M0:%.11e Cuc:%.6e e:%f Cus:%.6e sqrtA:%.11g "
  275. "toe:%lu FIT:%u AODO:%5u\n",
  276. subp->tSVID,
  277. subp->sub2.IODE,
  278. subp->sub2.d_Crs,
  279. subp->sub2.d_deltan,
  280. subp->sub2.d_M0,
  281. subp->sub2.d_Cuc,
  282. subp->sub2.d_eccentricity,
  283. subp->sub2.d_Cus,
  284. subp->sub2.d_sqrtA,
  285. subp->sub2.l_toe,
  286. subp->sub2.fit,
  287. subp->sub2.u_AODO);
  288. break;
  289. case 3:
  290. /* subframe 3: ephemeris for transmitting SV */
  291. subp->sub3.Cic = (int16_t)((words[2] >> 8) & 0x00FFFF);
  292. subp->sub3.d_Cic = pow(2.0, -29) * subp->sub3.Cic;
  293. subp->sub3.Omega0 = (int32_t)(words[2] & 0x0000FF);
  294. subp->sub3.Omega0 <<= 24;
  295. subp->sub3.Omega0 |= ( words[3] & 0x00FFFFFF);
  296. subp->sub3.d_Omega0 = pow(2.0, -31) * subp->sub3.Omega0;
  297. subp->sub3.Cis = (int16_t)((words[4] >> 8) & 0x00FFFF);
  298. subp->sub3.d_Cis = pow(2.0, -29) * subp->sub3.Cis;
  299. subp->sub3.i0 = (int32_t)(words[4] & 0x0000FF);
  300. subp->sub3.i0 <<= 24;
  301. subp->sub3.i0 |= ( words[5] & 0x00FFFFFF);
  302. subp->sub3.d_i0 = pow(2.0, -31) * subp->sub3.i0;
  303. subp->sub3.Crc = (int16_t)((words[6] >> 8) & 0x00FFFF);
  304. subp->sub3.d_Crc = pow(2.0, -5) * subp->sub3.Crc;
  305. subp->sub3.omega = (int32_t)(words[6] & 0x0000FF);
  306. subp->sub3.omega <<= 24;
  307. subp->sub3.omega |= ( words[7] & 0x00FFFFFF);
  308. subp->sub3.d_omega = pow(2.0, -31) * subp->sub3.omega;
  309. subp->sub3.Omegad = (int32_t)(words[8] & 0x00FFFFFF);
  310. subp->sub3.Omegad = uint2int(subp->sub3.Omegad, 24);
  311. subp->sub3.d_Omegad = pow(2.0, -43) * subp->sub3.Omegad;
  312. subp->sub3.IODE = ((words[9] >> 16) & 0x0000FF);
  313. subp->sub3.IDOT = (int16_t)((words[9] >> 2) & 0x003FFF);
  314. subp->sub3.IDOT = uint2int(subp->sub3.IDOT, 14);
  315. subp->sub3.d_IDOT = pow(2.0, -43) * subp->sub3.IDOT;
  316. GPSD_LOG(LOG_PROG, &session->context->errout,
  317. "50B: SF:3 SV:%2u IODE:%3u I IDOT:%.6g Cic:%.6e Omega0:%.11e "
  318. " Cis:%.7g i0:%.11e Crc:%.7g omega:%.11e Omegad:%.6e\n",
  319. subp->tSVID, subp->sub3.IODE, subp->sub3.d_IDOT,
  320. subp->sub3.d_Cic, subp->sub3.d_Omega0, subp->sub3.d_Cis,
  321. subp->sub3.d_i0, subp->sub3.d_Crc, subp->sub3.d_omega,
  322. subp->sub3.d_Omegad );
  323. break;
  324. case 4:
  325. {
  326. int i = 0; /* handy loop counter */
  327. int sv = -2;
  328. switch (subp->pageid) {
  329. case 0:
  330. /* almanac for dummy sat 0, which is same as transmitting sat */
  331. sv = 0;
  332. break;
  333. case 1:
  334. case 6:
  335. case 11:
  336. case 16:
  337. case 21:
  338. case 57:
  339. /* for some inscutable reason these pages are all sent
  340. * as page 57, IS-GPS-200E Table 20-V */
  341. break;
  342. case 12:
  343. case 24:
  344. case 62:
  345. /* for some inscrutable reason these pages are all sent
  346. * as page 62, IS-GPS-200E Table 20-V */
  347. break;
  348. case 14:
  349. case 53:
  350. /* for some inscrutable reason page 14 is sent
  351. * as page 53, IS-GPS-200E Table 20-V */
  352. break;
  353. case 15:
  354. case 54:
  355. /* for some inscrutable reason page 15 is sent
  356. * as page 54, IS-GPS-200E Table 20-V */
  357. break;
  358. case 19:
  359. /* for some inscrutable reason page 20 is sent
  360. * as page 58, IS-GPS-200E Table 20-V */
  361. /* reserved page */
  362. break;
  363. case 20:
  364. /* for some inscrutable reason page 20 is sent
  365. * as page 59, IS-GPS-200E Table 20-V */
  366. /* reserved page */
  367. break;
  368. case 22:
  369. case 60:
  370. /* for some inscrutable reason page 22 is sent
  371. * as page 60, IS-GPS-200E Table 20-V */
  372. /* reserved page */
  373. break;
  374. case 23:
  375. case 61:
  376. /* for some inscrutable reason page 23 is sent
  377. * as page 61, IS-GPS-200E Table 20-V */
  378. /* reserved page */
  379. break;
  380. /* almanac data for SV 25 through 32 respectively; */
  381. case 2:
  382. sv = 25;
  383. break;
  384. case 3:
  385. sv = 26;
  386. break;
  387. case 4:
  388. sv = 27;
  389. break;
  390. case 5:
  391. sv = 28;
  392. break;
  393. case 7:
  394. sv = 29;
  395. break;
  396. case 8:
  397. sv = 30;
  398. break;
  399. case 9:
  400. sv = 31;
  401. break;
  402. case 10:
  403. sv = 32;
  404. break;
  405. case 13:
  406. case 52:
  407. /* NMCT */
  408. sv = -1;
  409. subp->sub4_13.ai = (unsigned char)((words[2] >> 22) & 0x000003);
  410. subp->sub4_13.ERD[1] = (char)((words[2] >> 8) & 0x00003F);
  411. subp->sub4_13.ERD[2] = (char)((words[2] >> 2) & 0x00003F);
  412. subp->sub4_13.ERD[3] = (char)((words[2] >> 0) & 0x000003);
  413. subp->sub4_13.ERD[3] <<= 2;
  414. subp->sub4_13.ERD[3] |= (char)((words[3] >> 20) & 0x00000F);
  415. subp->sub4_13.ERD[4] = (char)((words[3] >> 14) & 0x00003F);
  416. subp->sub4_13.ERD[5] = (char)((words[3] >> 8) & 0x00003F);
  417. subp->sub4_13.ERD[6] = (char)((words[3] >> 2) & 0x00003F);
  418. subp->sub4_13.ERD[7] = (char)((words[3] >> 0) & 0x000003);
  419. subp->sub4_13.ERD[7] <<= 2;
  420. subp->sub4_13.ERD[7] |= (char)((words[4] >> 20) & 0x00000F);
  421. subp->sub4_13.ERD[8] = (char)((words[4] >> 14) & 0x00003F);
  422. subp->sub4_13.ERD[9] = (char)((words[4] >> 8) & 0x00003F);
  423. subp->sub4_13.ERD[10] = (char)((words[4] >> 2) & 0x00003F);
  424. subp->sub4_13.ERD[11] = (char)((words[4] >> 0) & 0x00000F);
  425. subp->sub4_13.ERD[11] <<= 2;
  426. subp->sub4_13.ERD[11] |= (char)((words[5] >> 20) & 0x00000F);
  427. subp->sub4_13.ERD[12] = (char)((words[5] >> 14) & 0x00003F);
  428. subp->sub4_13.ERD[13] = (char)((words[5] >> 8) & 0x00003F);
  429. subp->sub4_13.ERD[14] = (char)((words[5] >> 2) & 0x00003F);
  430. subp->sub4_13.ERD[15] = (char)((words[5] >> 0) & 0x000003);
  431. subp->sub4_13.ERD[15] <<= 2;
  432. subp->sub4_13.ERD[15] |= (char)((words[6] >> 20) & 0x00000F);
  433. subp->sub4_13.ERD[16] = (char)((words[6] >> 14) & 0x00003F);
  434. subp->sub4_13.ERD[17] = (char)((words[6] >> 8) & 0x00003F);
  435. subp->sub4_13.ERD[18] = (char)((words[6] >> 2) & 0x00003F);
  436. subp->sub4_13.ERD[19] = (char)((words[6] >> 0) & 0x000003);
  437. subp->sub4_13.ERD[19] <<= 2;
  438. subp->sub4_13.ERD[19] |= (char)((words[7] >> 20) & 0x00000F);
  439. subp->sub4_13.ERD[20] = (char)((words[7] >> 14) & 0x00003F);
  440. subp->sub4_13.ERD[21] = (char)((words[7] >> 8) & 0x00003F);
  441. subp->sub4_13.ERD[22] = (char)((words[7] >> 2) & 0x00003F);
  442. subp->sub4_13.ERD[23] = (char)((words[7] >> 0) & 0x000003);
  443. subp->sub4_13.ERD[23] <<= 2;
  444. subp->sub4_13.ERD[23] |= (char)((words[8] >> 20) & 0x00000F);
  445. subp->sub4_13.ERD[24] = (char)((words[8] >> 14) & 0x00003F);
  446. subp->sub4_13.ERD[25] = (char)((words[8] >> 8) & 0x00003F);
  447. subp->sub4_13.ERD[26] = (char)((words[8] >> 2) & 0x00003F);
  448. subp->sub4_13.ERD[27] = (char)((words[8] >> 0) & 0x000003);
  449. subp->sub4_13.ERD[27] <<= 2;
  450. subp->sub4_13.ERD[27] |= (char)((words[9] >> 20) & 0x00000F);
  451. subp->sub4_13.ERD[28] = (char)((words[9] >> 14) & 0x00003F);
  452. subp->sub4_13.ERD[29] = (char)((words[9] >> 8) & 0x00003F);
  453. subp->sub4_13.ERD[30] = (char)((words[9] >> 2) & 0x00003F);
  454. for ( i = 1; i < 31; i++ ) {
  455. subp->sub4_13.ERD[i] = uint2int(subp->sub4_13.ERD[i], 6);
  456. }
  457. GPSD_LOG(LOG_PROG, &session->context->errout,
  458. "50B: SF:4-13 data_id %d ai:%u "
  459. "ERD1:%d ERD2:%d ERD3:%d ERD4:%d "
  460. "ERD5:%d ERD6:%d ERD7:%d ERD8:%d "
  461. "ERD9:%d ERD10:%d ERD11:%d ERD12:%d "
  462. "ERD13:%d ERD14:%d ERD15:%d ERD16:%d "
  463. "ERD17:%d ERD18:%d ERD19:%d ERD20:%d "
  464. "ERD21:%d ERD22:%d ERD23:%d ERD24:%d "
  465. "ERD25:%d ERD26:%d ERD27:%d ERD28:%d "
  466. "ERD29:%d ERD30:%d\n",
  467. subp->data_id, subp->sub4_13.ai,
  468. subp->sub4_13.ERD[1], subp->sub4_13.ERD[2],
  469. subp->sub4_13.ERD[3], subp->sub4_13.ERD[4],
  470. subp->sub4_13.ERD[5], subp->sub4_13.ERD[6],
  471. subp->sub4_13.ERD[7], subp->sub4_13.ERD[8],
  472. subp->sub4_13.ERD[9], subp->sub4_13.ERD[10],
  473. subp->sub4_13.ERD[11], subp->sub4_13.ERD[12],
  474. subp->sub4_13.ERD[13], subp->sub4_13.ERD[14],
  475. subp->sub4_13.ERD[15], subp->sub4_13.ERD[16],
  476. subp->sub4_13.ERD[17], subp->sub4_13.ERD[18],
  477. subp->sub4_13.ERD[19], subp->sub4_13.ERD[20],
  478. subp->sub4_13.ERD[21], subp->sub4_13.ERD[22],
  479. subp->sub4_13.ERD[23], subp->sub4_13.ERD[24],
  480. subp->sub4_13.ERD[25], subp->sub4_13.ERD[26],
  481. subp->sub4_13.ERD[27], subp->sub4_13.ERD[28],
  482. subp->sub4_13.ERD[29], subp->sub4_13.ERD[30]);
  483. break;
  484. case 25:
  485. case 63:
  486. /* for some inscrutable reason page 25 is sent
  487. * as page 63, IS-GPS-200E Table 20-V */
  488. /* A-S flags/SV configurations for 32 SVs,
  489. * plus SV health for SV 25 through 32
  490. */
  491. sv = -1;
  492. subp->sub4_25.svf[1] = (unsigned char)((words[2] >> 12) & 0x0F);
  493. subp->sub4_25.svf[2] = (unsigned char)((words[2] >> 8) & 0x0F);
  494. subp->sub4_25.svf[3] = (unsigned char)((words[2] >> 4) & 0x0F);
  495. subp->sub4_25.svf[4] = (unsigned char)((words[2] >> 0) & 0x0F);
  496. subp->sub4_25.svf[5] = (unsigned char)((words[3] >> 20) & 0x0F);
  497. subp->sub4_25.svf[6] = (unsigned char)((words[3] >> 16) & 0x0F);
  498. subp->sub4_25.svf[7] = (unsigned char)((words[3] >> 12) & 0x0F);
  499. subp->sub4_25.svf[8] = (unsigned char)((words[3] >> 8) & 0x0F);
  500. subp->sub4_25.svf[9] = (unsigned char)((words[3] >> 4) & 0x0F);
  501. subp->sub4_25.svf[10] = (unsigned char)((words[3] >> 0) & 0x0F);
  502. subp->sub4_25.svf[11] = (unsigned char)((words[4] >> 20) & 0x0F);
  503. subp->sub4_25.svf[12] = (unsigned char)((words[4] >> 16) & 0x0F);
  504. subp->sub4_25.svf[13] = (unsigned char)((words[4] >> 12) & 0x0F);
  505. subp->sub4_25.svf[14] = (unsigned char)((words[4] >> 8) & 0x0F);
  506. subp->sub4_25.svf[15] = (unsigned char)((words[4] >> 4) & 0x0F);
  507. subp->sub4_25.svf[16] = (unsigned char)((words[4] >> 0) & 0x0F);
  508. subp->sub4_25.svf[17] = (unsigned char)((words[5] >> 20) & 0x0F);
  509. subp->sub4_25.svf[18] = (unsigned char)((words[5] >> 16) & 0x0F);
  510. subp->sub4_25.svf[19] = (unsigned char)((words[5] >> 12) & 0x0F);
  511. subp->sub4_25.svf[20] = (unsigned char)((words[5] >> 8) & 0x0F);
  512. subp->sub4_25.svf[21] = (unsigned char)((words[5] >> 4) & 0x0F);
  513. subp->sub4_25.svf[22] = (unsigned char)((words[5] >> 0) & 0x0F);
  514. subp->sub4_25.svf[23] = (unsigned char)((words[6] >> 20) & 0x0F);
  515. subp->sub4_25.svf[24] = (unsigned char)((words[6] >> 16) & 0x0F);
  516. subp->sub4_25.svf[25] = (unsigned char)((words[6] >> 12) & 0x0F);
  517. subp->sub4_25.svf[26] = (unsigned char)((words[6] >> 8) & 0x0F);
  518. subp->sub4_25.svf[27] = (unsigned char)((words[6] >> 4) & 0x0F);
  519. subp->sub4_25.svf[28] = (unsigned char)((words[6] >> 0) & 0x0F);
  520. subp->sub4_25.svf[29] = (unsigned char)((words[7] >> 20) & 0x0F);
  521. subp->sub4_25.svf[30] = (unsigned char)((words[7] >> 16) & 0x0F);
  522. subp->sub4_25.svf[31] = (unsigned char)((words[7] >> 12) & 0x0F);
  523. subp->sub4_25.svf[32] = (unsigned char)((words[7] >> 8) & 0x0F);
  524. subp->sub4_25.svhx[0] = ((words[7] >> 0) & 0x00003F);
  525. subp->sub4_25.svhx[1] = ((words[8] >> 18) & 0x00003F);
  526. subp->sub4_25.svhx[2] = ((words[8] >> 12) & 0x00003F);
  527. subp->sub4_25.svhx[3] = ((words[8] >> 6) & 0x00003F);
  528. subp->sub4_25.svhx[4] = ((words[8] >> 0) & 0x00003F);
  529. subp->sub4_25.svhx[5] = ((words[9] >> 18) & 0x00003F);
  530. subp->sub4_25.svhx[6] = ((words[9] >> 12) & 0x00003F);
  531. subp->sub4_25.svhx[7] = ((words[9] >> 6) & 0x00003F);
  532. GPSD_LOG(LOG_PROG, &session->context->errout,
  533. "50B: SF:4-25 data_id %d "
  534. "SV1:%u SV2:%u SV3:%u SV4:%u "
  535. "SV5:%u SV6:%u SV7:%u SV8:%u "
  536. "SV9:%u SV10:%u SV11:%u SV12:%u "
  537. "SV13:%u SV14:%u SV15:%u SV16:%u "
  538. "SV17:%u SV18:%u SV19:%u SV20:%u "
  539. "SV21:%u SV22:%u SV23:%u SV24:%u "
  540. "SV25:%u SV26:%u SV27:%u SV28:%u "
  541. "SV29:%u SV30:%u SV31:%u SV32:%u "
  542. "SVH25:%u SVH26:%u SVH27:%u SVH28:%u "
  543. "SVH29:%u SVH30:%u SVH31:%u SVH32:%u\n",
  544. subp->data_id,
  545. subp->sub4_25.svf[1], subp->sub4_25.svf[2],
  546. subp->sub4_25.svf[3], subp->sub4_25.svf[4],
  547. subp->sub4_25.svf[5], subp->sub4_25.svf[6],
  548. subp->sub4_25.svf[7], subp->sub4_25.svf[8],
  549. subp->sub4_25.svf[9], subp->sub4_25.svf[10],
  550. subp->sub4_25.svf[11], subp->sub4_25.svf[12],
  551. subp->sub4_25.svf[13], subp->sub4_25.svf[14],
  552. subp->sub4_25.svf[15], subp->sub4_25.svf[16],
  553. subp->sub4_25.svf[17], subp->sub4_25.svf[18],
  554. subp->sub4_25.svf[19], subp->sub4_25.svf[20],
  555. subp->sub4_25.svf[21], subp->sub4_25.svf[22],
  556. subp->sub4_25.svf[23], subp->sub4_25.svf[24],
  557. subp->sub4_25.svf[25], subp->sub4_25.svf[26],
  558. subp->sub4_25.svf[27], subp->sub4_25.svf[28],
  559. subp->sub4_25.svf[29], subp->sub4_25.svf[30],
  560. subp->sub4_25.svf[31], subp->sub4_25.svf[32],
  561. subp->sub4_25.svhx[0], subp->sub4_25.svhx[1],
  562. subp->sub4_25.svhx[2], subp->sub4_25.svhx[3],
  563. subp->sub4_25.svhx[4], subp->sub4_25.svhx[5],
  564. subp->sub4_25.svhx[6], subp->sub4_25.svhx[7]);
  565. break;
  566. case 33:
  567. case 34:
  568. case 35:
  569. case 36:
  570. case 37:
  571. case 38:
  572. case 39:
  573. case 40:
  574. case 41:
  575. case 42:
  576. case 43:
  577. case 44:
  578. case 45:
  579. case 46:
  580. case 47:
  581. case 48:
  582. case 49:
  583. case 50:
  584. /* unassigned */
  585. break;
  586. case 51:
  587. /* unknown */
  588. break;
  589. case 17:
  590. case 55:
  591. /* for some inscrutable reason page 17 is sent
  592. * as page 55, IS-GPS-200E Table 20-V */
  593. sv = -1;
  594. /*
  595. * "The requisite 176 bits shall occupy bits 9 through 24
  596. * of word TWO, the 24 MSBs of words THREE through EIGHT,
  597. * plus the 16 MSBs of word NINE." (word numbers changed
  598. * to account for zero-indexing)
  599. * Since we've already stripped the low six parity bits,
  600. * and shifted the data to a byte boundary, we can just
  601. * copy it out. */
  602. i = 0;
  603. subp->sub4_17.str[i++] = (words[2] >> 8) & 0xff;
  604. subp->sub4_17.str[i++] = (words[2]) & 0xff;
  605. subp->sub4_17.str[i++] = (words[3] >> 16) & 0xff;
  606. subp->sub4_17.str[i++] = (words[3] >> 8) & 0xff;
  607. subp->sub4_17.str[i++] = (words[3]) & 0xff;
  608. subp->sub4_17.str[i++] = (words[4] >> 16) & 0xff;
  609. subp->sub4_17.str[i++] = (words[4] >> 8) & 0xff;
  610. subp->sub4_17.str[i++] = (words[4]) & 0xff;
  611. subp->sub4_17.str[i++] = (words[5] >> 16) & 0xff;
  612. subp->sub4_17.str[i++] = (words[5] >> 8) & 0xff;
  613. subp->sub4_17.str[i++] = (words[5]) & 0xff;
  614. subp->sub4_17.str[i++] = (words[6] >> 16) & 0xff;
  615. subp->sub4_17.str[i++] = (words[6] >> 8) & 0xff;
  616. subp->sub4_17.str[i++] = (words[6]) & 0xff;
  617. subp->sub4_17.str[i++] = (words[7] >> 16) & 0xff;
  618. subp->sub4_17.str[i++] = (words[7] >> 8) & 0xff;
  619. subp->sub4_17.str[i++] = (words[7]) & 0xff;
  620. subp->sub4_17.str[i++] = (words[8] >> 16) & 0xff;
  621. subp->sub4_17.str[i++] = (words[8] >> 8) & 0xff;
  622. subp->sub4_17.str[i++] = (words[8]) & 0xff;
  623. subp->sub4_17.str[i++] = (words[9] >> 16) & 0xff;
  624. subp->sub4_17.str[i++] = (words[9] >> 8) & 0xff;
  625. subp->sub4_17.str[i] = '\0';
  626. GPSD_LOG(LOG_PROG, &session->context->errout,
  627. "50B: SF:4-17 system message: %.24s\n",
  628. subp->sub4_17.str);
  629. break;
  630. case 18:
  631. case 56:
  632. /* for some inscrutable reason page 18 is sent
  633. * as page 56, IS-GPS-200E Table 20-V */
  634. /* ionospheric and UTC data */
  635. sv = -1;
  636. /* current leap seconds */
  637. subp->sub4_18.alpha0 = (int8_t)((words[2] >> 8) & 0x0000FF);
  638. subp->sub4_18.d_alpha0 = pow(2.0, -30) *
  639. (int)subp->sub4_18.alpha0;
  640. subp->sub4_18.alpha1 = (int8_t)((words[2] >> 0) & 0x0000FF);
  641. subp->sub4_18.d_alpha1 = pow(2.0, -27) *
  642. (int)subp->sub4_18.alpha1;
  643. subp->sub4_18.alpha2 = (int8_t)((words[3] >> 16) & 0x0000FF);
  644. subp->sub4_18.d_alpha2 = pow(2.0, -24) *
  645. (int)subp->sub4_18.alpha2;
  646. subp->sub4_18.alpha3 = (int8_t)((words[3] >> 8) & 0x0000FF);
  647. subp->sub4_18.d_alpha3 = pow(2.0, -24) *
  648. (int)subp->sub4_18.alpha3;
  649. subp->sub4_18.beta0 = (int8_t)((words[3] >> 0) & 0x0000FF);
  650. subp->sub4_18.d_beta0 = pow(2.0, 11) * (int)subp->sub4_18.beta0;
  651. subp->sub4_18.beta1 = (int8_t)((words[4] >> 16) & 0x0000FF);
  652. subp->sub4_18.d_beta1 = pow(2.0, 14) * (int)subp->sub4_18.beta1;
  653. subp->sub4_18.beta2 = (int8_t)((words[4] >> 8) & 0x0000FF);
  654. subp->sub4_18.d_beta2 = pow(2.0, 16) * (int)subp->sub4_18.beta2;
  655. subp->sub4_18.beta3 = (int8_t)((words[4] >> 0) & 0x0000FF);
  656. subp->sub4_18.d_beta3 = pow(2.0, 16) * (int)subp->sub4_18.beta3;
  657. subp->sub4_18.A1 = (int32_t)((words[5] >> 0) & 0xFFFFFF);
  658. subp->sub4_18.A1 = uint2int(subp->sub4_18.A1, 24);
  659. subp->sub4_18.d_A1 = pow(2.0,-50) * subp->sub4_18.A1;
  660. subp->sub4_18.A0 = (int32_t)((words[6] >> 0) & 0xFFFFFF);
  661. subp->sub4_18.A0 <<= 8;
  662. subp->sub4_18.A0 |= ((words[7] >> 16) & 0x0000FF);
  663. subp->sub4_18.d_A0 = pow(2.0,-30) * subp->sub4_18.A0;
  664. /* careful WN is 10 bits, but WNt is 8 bits! */
  665. /* WNt (Week Number of LSF) */
  666. subp->sub4_18.tot = ((words[7] >> 8) & 0x0000FF);
  667. subp->sub4_18.t_tot = 2e12 * subp->sub4_18.tot;
  668. subp->sub4_18.WNt = ((words[7] >> 0) & 0x0000FF);
  669. subp->sub4_18.leap = (int8_t)((words[8] >> 16) & 0x0000FF);
  670. subp->sub4_18.WNlsf = ((words[8] >> 8) & 0x0000FF);
  671. /* DN (Day Number of LSF) */
  672. subp->sub4_18.DN = (words[8] & 0x0000FF);
  673. /* leap second future */
  674. subp->sub4_18.lsf = (int8_t)((words[9] >> 16) & 0x0000FF);
  675. GPSD_LOG(LOG_PROG, &session->context->errout,
  676. "50B: SF:4-18 a0:%.5g a1:%.5g a2:%.5g a3:%.5g "
  677. "b0:%.5g b1:%.5g b2:%.5g b3:%.5g "
  678. "A1:%.11e A0:%.11e tot:%lld WNt:%u "
  679. "ls: %d WNlsf:%u DN:%u, lsf:%d\n",
  680. subp->sub4_18.d_alpha0, subp->sub4_18.d_alpha1,
  681. subp->sub4_18.d_alpha2, subp->sub4_18.d_alpha3,
  682. subp->sub4_18.d_beta0, subp->sub4_18.d_beta1,
  683. subp->sub4_18.d_beta2, subp->sub4_18.d_beta3,
  684. subp->sub4_18.d_A1, subp->sub4_18.d_A0,
  685. (long long)subp->sub4_18.t_tot, subp->sub4_18.WNt,
  686. subp->sub4_18.leap, subp->sub4_18.WNlsf,
  687. subp->sub4_18.DN, subp->sub4_18.lsf);
  688. /* notify the leap seconds correction in the end
  689. * of current day */
  690. /* IS-GPS-200 Revision E, paragraph 20.3.3.5.2.4 */
  691. /* FIXME: only allow LEAPs in June and December */
  692. // only need to check whole seconds
  693. if (((session->context->gps_week % 256) ==
  694. (unsigned short)subp->sub4_18.WNlsf) &&
  695. (((subp->sub4_18.DN - 1) * SECS_PER_DAY) <
  696. session->context->gps_tow.tv_sec) &&
  697. ((subp->sub4_18.DN * SECS_PER_DAY) >
  698. session->context->gps_tow.tv_sec)) {
  699. if (subp->sub4_18.leap < subp->sub4_18.lsf) {
  700. session->context->leap_notify = LEAP_ADDSECOND;
  701. } else if (subp->sub4_18.leap > subp->sub4_18.lsf) {
  702. session->context->leap_notify = LEAP_DELSECOND;
  703. } else {
  704. session->context->leap_notify = LEAP_NOWARNING;
  705. }
  706. } else {
  707. session->context->leap_notify = LEAP_NOWARNING;
  708. }
  709. session->context->leap_seconds = (int)subp->sub4_18.leap;
  710. session->context->valid |= LEAP_SECOND_VALID;
  711. break;
  712. default:
  713. ; /* no op */
  714. }
  715. if ( -1 < sv ) {
  716. subp->is_almanac = 1;
  717. subframe_almanac(&session->context->errout,
  718. subp->tSVID, words, subp->subframe_num,
  719. (uint8_t)sv, subp->data_id,
  720. &subp->sub4.almanac);
  721. } else if ( -2 == sv ) {
  722. /* unknown or secret page */
  723. GPSD_LOG(LOG_PROG, &session->context->errout,
  724. "50B: SF:4-%d data_id %d\n",
  725. subp->pageid, subp->data_id);
  726. return 0;
  727. }
  728. /* else, already handled */
  729. }
  730. break;
  731. case 5:
  732. /* Pages 0, dummy almanac for dummy SV 0
  733. * Pages 1 through 24: almanac data for SV 1 through 24
  734. * Page 25: SV health data for SV 1 through 24, the almanac
  735. * reference time, the almanac reference week number.
  736. */
  737. if ( 25 > subp->pageid ) {
  738. subp->is_almanac = 1;
  739. subframe_almanac(&session->context->errout,
  740. subp->tSVID, words, subp->subframe_num,
  741. subp->pageid, subp->data_id, &subp->sub5.almanac);
  742. } else if ( 51 == subp->pageid ) {
  743. /* for some inscrutable reason page 25 is sent as page 51
  744. * IS-GPS-200E Table 20-V */
  745. subp->sub5_25.toa = ((words[2] >> 8) & 0x0000FF);
  746. subp->sub5_25.l_toa <<= 12;
  747. subp->sub5_25.WNa = ( words[2] & 0x0000FF);
  748. subp->sub5_25.sv[1] = ((words[2] >> 18) & 0x00003F);
  749. subp->sub5_25.sv[2] = ((words[2] >> 12) & 0x00003F);
  750. subp->sub5_25.sv[3] = ((words[2] >> 6) & 0x00003F);
  751. subp->sub5_25.sv[4] = ((words[2] >> 0) & 0x00003F);
  752. subp->sub5_25.sv[5] = ((words[3] >> 18) & 0x00003F);
  753. subp->sub5_25.sv[6] = ((words[3] >> 12) & 0x00003F);
  754. subp->sub5_25.sv[7] = ((words[3] >> 6) & 0x00003F);
  755. subp->sub5_25.sv[8] = ((words[3] >> 0) & 0x00003F);
  756. subp->sub5_25.sv[9] = ((words[4] >> 18) & 0x00003F);
  757. subp->sub5_25.sv[10] = ((words[4] >> 12) & 0x00003F);
  758. subp->sub5_25.sv[11] = ((words[4] >> 6) & 0x00003F);
  759. subp->sub5_25.sv[12] = ((words[4] >> 0) & 0x00003F);
  760. subp->sub5_25.sv[13] = ((words[5] >> 18) & 0x00003F);
  761. subp->sub5_25.sv[14] = ((words[5] >> 12) & 0x00003F);
  762. subp->sub5_25.sv[15] = ((words[5] >> 6) & 0x00003F);
  763. subp->sub5_25.sv[16] = ((words[5] >> 0) & 0x00003F);
  764. subp->sub5_25.sv[17] = ((words[6] >> 18) & 0x00003F);
  765. subp->sub5_25.sv[18] = ((words[6] >> 12) & 0x00003F);
  766. subp->sub5_25.sv[19] = ((words[6] >> 6) & 0x00003F);
  767. subp->sub5_25.sv[20] = ((words[6] >> 0) & 0x00003F);
  768. subp->sub5_25.sv[21] = ((words[7] >> 18) & 0x00003F);
  769. subp->sub5_25.sv[22] = ((words[7] >> 12) & 0x00003F);
  770. subp->sub5_25.sv[23] = ((words[7] >> 6) & 0x00003F);
  771. subp->sub5_25.sv[24] = ((words[7] >> 0) & 0x00003F);
  772. GPSD_LOG(LOG_PROG, &session->context->errout,
  773. "50B: SF:5-25 SV:%2u ID:%u toa:%lu WNa:%u "
  774. "SV1:%u SV2:%u SV3:%u SV4:%u "
  775. "SV5:%u SV6:%u SV7:%u SV8:%u "
  776. "SV9:%u SV10:%u SV11:%u SV12:%u "
  777. "SV13:%u SV14:%u SV15:%u SV16:%u "
  778. "SV17:%u SV18:%u SV19:%u SV20:%u "
  779. "SV21:%u SV22:%u SV23:%u SV24:%u\n",
  780. subp->tSVID, subp->data_id,
  781. subp->sub5_25.l_toa, subp->sub5_25.WNa,
  782. subp->sub5_25.sv[1], subp->sub5_25.sv[2],
  783. subp->sub5_25.sv[3], subp->sub5_25.sv[4],
  784. subp->sub5_25.sv[5], subp->sub5_25.sv[6],
  785. subp->sub5_25.sv[7], subp->sub5_25.sv[8],
  786. subp->sub5_25.sv[9], subp->sub5_25.sv[10],
  787. subp->sub5_25.sv[11], subp->sub5_25.sv[12],
  788. subp->sub5_25.sv[13], subp->sub5_25.sv[14],
  789. subp->sub5_25.sv[15], subp->sub5_25.sv[16],
  790. subp->sub5_25.sv[17], subp->sub5_25.sv[18],
  791. subp->sub5_25.sv[19], subp->sub5_25.sv[20],
  792. subp->sub5_25.sv[21], subp->sub5_25.sv[22],
  793. subp->sub5_25.sv[23], subp->sub5_25.sv[24]);
  794. } else {
  795. /* unknown page */
  796. GPSD_LOG(LOG_PROG, &session->context->errout,
  797. "50B: SF:5-%d data_id %d unknown page\n",
  798. subp->pageid, subp->data_id);
  799. return 0;
  800. }
  801. break;
  802. default:
  803. /* unknown/illegal subframe */
  804. return 0;
  805. }
  806. return SUBFRAME_SET;
  807. }
  808. // vim: set expandtab shiftwidth=4