1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
/* $Id: subframe.c 3770 2006-11-02 05:07:11Z esr $ */
/* subframe.c -- interpret satellite subframe data. */
#include <sys/types.h>
#include "gpsd_config.h"
#include "gpsd.h"
/*@ -usedef @*/
void gpsd_interpret_subframe(struct gps_device_t *session,unsigned int words[])
/* extract leap-second from RTCM-104 subframe data */
{
/*
* Heavy black magic begins here!
*
* A description of how to decode these bits is at
* <http://home-2.worldonline.nl/~samsvl/nav2eu.htm>
*
* We're after subframe 4 page 18 word 9, the leap year correction.
* We assume that the chip is presenting clean data that has been
* parity-checked.
*
* To date this code has been tested only on SiRFs. It's in the
* core because other chipsets reporting only GPS time but with
* the capability to read subframe data may want it.
*/
int i;
unsigned int pageid, subframe, leap;
gpsd_report(LOG_IO,
"50B (raw): %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
words[0], words[1], words[2], words[3], words[4],
words[5], words[6], words[7], words[8], words[9]);
/*
* Mask off the high 2 bits and shift out the 6 parity bits.
* Once we've filtered, we can ignore the TEL and HOW words.
* We don't need to check parity here, the SiRF chipset does
* that and throws a subframe error if the parity is wrong.
*/
for (i = 0; i < 10; i++)
words[i] = (words[i] & 0x3fffffff) >> 6;
/*
* "First, throw away everything that doesn't start with 8b or
* 74. more correctly the first byte should be 10001011. If
* it's 01110100, then you have a subframe with inverted
* polarity and each byte needs to be xored against 0xff to
* remove the inversion."
*/
words[0] &= 0xff0000;
if (words[0] != 0x8b0000 && words[0] != 0x740000)
return;
if (words[0] == 0x740000)
for (i = 1; i < 10; i++)
words[i] ^= 0xffffff;
/*
* The subframe ID is in the Hand Over Word (page 80)
*/
subframe = ((words[1] >> 2) & 0x07);
/* we're not interested in anything but subframe 4 */
if (subframe != 4)
return;
/*
* Pages 66-76a,80 of ICD-GPS-200 are the subframe structures.
* Subframe 4 page 18 is on page 74.
* See page 105 for the mapping between magic SVIDs and pages.
*/
pageid = (words[2] & 0x3F0000) >> 16;
gpsd_report(LOG_PROG, "Subframe 4 SVID is %d\n", pageid);
if (pageid == 56) { /* magic SVID for page 18 */
/* once we've filtered, we can ignore the TEL and HOW words */
gpsd_report(LOG_PROG, "50B: SF=%d %06x %06x %06x %06x %06x %06x %06x %06x\n",
subframe,
words[2], words[3], words[4], words[5],
words[6], words[7], words[8], words[9]);
leap = (words[8] & 0xff0000) >> 16;
/*
* On SiRFs, there appears to be some bizarre bug that
* randomly causes this field to come out two's-complemented.
* This could very well be a general problem; work around it.
* At the current expected rate of issuing leap-seconds this
* kluge won't bite until about 2070, by which time the
* vendors had better have fixed their damn firmware...
*
* Carl: ...I am unsure, and suggest you
* experiment. The D30 bit is in bit 30 of the 32-bit
* word (next to MSB), and should signal an inverted
* value when it is one coming over the air. But if
* the bit is set and the word decodes right without
* inversion, then we properly caught it. Cases where
* you see subframe 6 rather than 1 means we should
* have done the inversion but we did not. Some other
* things you can watch for: in any subframe, the
* second word (HOW word) should have last 2 parity
* bits 00 -- there are bits within the rest of the
* word that are set as required to ensure that. The
* same goes for word 10. That means that both words
* 1 and 3 (the words that immediately follow words 10
* and 2, respectively) should always be uninverted.
* In these cases, the D29 and D30 from the previous
* words, found in the two MSBs of the word, should
* show 00 -- if they don't then you may find an
* unintended inversion due to noise on the data link.
*/
if (leap > 128)
leap ^= 0xff;
gpsd_report(LOG_INF, "leap-seconds is %d\n", leap);
session->context->leap_seconds = (int)leap;
session->context->valid |= LEAP_SECOND_VALID;
}
}
/*@ +usedef @*/
|