Devuan fork of gpsd
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

852 lines
32 KiB

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