summaryrefslogtreecommitdiff
path: root/subframe.c
blob: 7b2633a4ca523681552ef0baaea12f572a632ffd (plain)
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* $Id$ */
/* subframe.c -- interpret satellite subframe data. */
#include <sys/types.h>

#include "gpsd.h"

#if 0
static char sf4map[] = {-1, 57, 25, 26, 27, 28, 57, 29, 30, 31, 32, 57, 62, 52, 53, 54, 57, 55, 56, 58, 59, 57, 60, 61, 62, 63};
static char sf5map[] = {-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 51};
#endif

/*@ -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 second correction.
     * We assume that the chip is presenting clean data that has been
     * parity-checked.
     *
     * To date this code has been tested on iTrax, SiRF and ublox. It's in
     * the core because other chipsets reporting only GPS time but with the
     * capability to read subframe data may want it.
     */
    unsigned int pageid, subframe, data_id, 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]);
    /*
     * It is the responsibility of each driver to mask off the high 2 bits
     * and shift out the 6 parity bits or do whatever else is necessary to
     * present an array of acceptable words to this decoder. Hopefully parity
     * checks are done in hardware, but if not, the driver should do them.
     */

    /* The subframe ID is in the Hand Over Word (page 80) */
    subframe = ((words[1] >> 2) & 0x07);
    /*
     * Consult the latest revision of IS-GPS-200 for the mapping
     * between magic SVIDs and pages.
     */
    pageid = (words[2] & 0x3F0000) >> 16;
    data_id = (words[2] >> 22) & 0x3;
    gpsd_report(LOG_PROG, "Subframe %d SVID %d data_id %d\n", subframe, pageid, data_id);
    /* we're not interested in anything but subframe 4 - for now*/
    if (subframe != 4)
	return;
    /* once we've filtered, we can ignore the TEL and HOW words */
    gpsd_report(LOG_PROG, "50B: %06x %06x %06x %06x %06x %06x %06x %06x\n",
	    words[2], words[3], words[4], words[5],
	    words[6], words[7], words[8], words[9]);
    switch(pageid) {
    case 55:
	/*
	 * "The requisite 176 bits shall occupy bits 9 through 24 of word
	 * TWO, the 24 MSBs of words THREE through EIGHT, plus the 16 MSBs
	 * of word NINE." (word numbers changed to account for zero-indexing)
	 *
	 * Since we've already stripped the low six parity bits, and shifted
	 * the data to a byte boundary, we can just copy it out. */
	{
	char str[24];
	int j = 0;
	/*@ -type @*/
	str[j++] = (words[2] >> 8) & 0xff;
	str[j++] = (words[2]) & 0xff;

	str[j++] = (words[3] >> 16) & 0xff;
	str[j++] = (words[3] >> 8) & 0xff;
	str[j++] = (words[3]) & 0xff;

	str[j++] = (words[4] >> 16) & 0xff;
	str[j++] = (words[4] >> 8) & 0xff;
	str[j++] = (words[4]) & 0xff;

	str[j++] = (words[5] >> 16) & 0xff;
	str[j++] = (words[5] >> 8) & 0xff;
	str[j++] = (words[5]) & 0xff;

	str[j++] = (words[6] >> 16) & 0xff;
	str[j++] = (words[6] >> 8) & 0xff;
	str[j++] = (words[6]) & 0xff;

	str[j++] = (words[7] >> 16) & 0xff;
	str[j++] = (words[7] >> 8) & 0xff;
	str[j++] = (words[7]) & 0xff;

	str[j++] = (words[8] >> 16) & 0xff;
	str[j++] = (words[8] >> 8) & 0xff;
	str[j++] = (words[8]) & 0xff;

	str[j++] = (words[9] >> 16) & 0xff;
	str[j++] = (words[9] >> 8) & 0xff;
	str[j++] = '\0';
	/*@ +type @*/
	gpsd_report(LOG_INF, "gps system message is %s\n", str);
	}
	break;
    case 56:
	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;
	break;
    default:
	    ; /* no op */
    }
}
/*@ +usedef @*/