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