summaryrefslogtreecommitdiffstats
path: root/gps.c
blob: c23574840c5358ca8ad5ae0edce2c8774ee60374 (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
/*
 * gps.c
 *
 * A few utility functions to deal with low level GPS data
 *
 *
 * Copyright (C) 2009  Sylvain Munaut <tnt@246tNt.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "gps.h"


#define GET_FIELD_U(w, nb, pos) (((w) >> (pos)) & ((1<<(nb))-1))
#define GET_FIELD_S(w, nb, pos) (((int)((w) << (32-(nb)-(pos)))) >> (32-(nb)))

/*
 * Unpacks GPS Subframe 1,2,3 payloads (3 * 8 words)
 *
 * Note: eph->sv_id is not filled here since not present in those subframes
 *
 * (no parity bit checking is done, only the lower 24 bits of each word
 *  are used)
 */
int
gps_unpack_sf123(uint32_t *sf, struct gps_ephemeris_sv *eph)
{
	uint32_t *sf1 = &sf[0];
	uint32_t *sf2 = &sf[8];
	uint32_t *sf3 = &sf[16];

	int iode1, iode2;

	eph->week_no	= GET_FIELD_U(sf1[0], 10, 14);
	eph->code_on_l2	= GET_FIELD_U(sf1[0],  2, 12);
	eph->sv_ura	= GET_FIELD_U(sf1[0],  4,  8);
	eph->sv_health	= GET_FIELD_U(sf1[0],  6,  2);
	eph->l2_p_flag	= GET_FIELD_U(sf1[1],  1, 23);
	eph->t_gd	= GET_FIELD_S(sf1[4],  8,  0);
	eph->iodc	= (GET_FIELD_U(sf1[0],  2,  0) << 8) | \
	                   GET_FIELD_U(sf1[5],  8, 16);
	eph->t_oc	= GET_FIELD_U(sf1[5], 16,  0);
	eph->a_f2	= GET_FIELD_S(sf1[6],  8, 16);
	eph->a_f1	= GET_FIELD_S(sf1[6], 16,  0);
	eph->a_f0	= GET_FIELD_S(sf1[7], 22,  2);

	iode1		= GET_FIELD_U(sf2[0],  8, 16);
	eph->c_rs	= GET_FIELD_S(sf2[0], 16,  0);
	eph->delta_n	= GET_FIELD_S(sf2[1], 16,  8);
	eph->m_0	= (GET_FIELD_S(sf2[1],  8,  0) << 24) | \
	                   GET_FIELD_U(sf2[2], 24,  0);
	eph->c_uc	= GET_FIELD_S(sf2[3], 16,  8);
	eph->e		= (GET_FIELD_U(sf2[3],  8,  0) << 24) | \
	                   GET_FIELD_U(sf2[4], 24,  0);
	eph->c_us	= GET_FIELD_S(sf2[5], 16,  8);
	eph->a_powhalf	= (GET_FIELD_U(sf2[5],  8,  0) << 24) | \
	                   GET_FIELD_U(sf2[6], 24,  0);
	eph->t_oe	= GET_FIELD_U(sf2[7], 16,  8);
	eph->fit_flag	= GET_FIELD_U(sf2[7],  1,  7);

	eph->c_ic	= GET_FIELD_S(sf3[0], 16,  8);
	eph->omega_0	= (GET_FIELD_S(sf3[0],  8,  0) << 24) | \
	                   GET_FIELD_U(sf3[1], 24,  0);
	eph->c_is	= GET_FIELD_S(sf3[2], 16,  8);
	eph->i_0	= (GET_FIELD_S(sf3[2],  8,  0) << 24) | \
	                   GET_FIELD_U(sf3[3], 24,  0);
	eph->c_rc	= GET_FIELD_S(sf3[4], 16,  8);
	eph->w		= (GET_FIELD_S(sf3[4],  8,  0) << 24) | \
	                   GET_FIELD_U(sf3[5], 24,  0);
	eph->omega_dot	= GET_FIELD_S(sf3[6], 24,  0);
	iode2		= GET_FIELD_U(sf3[7],  8, 16);
	eph->idot	= GET_FIELD_S(sf3[7], 14,  2);

	eph->_rsvd1	= GET_FIELD_U(sf1[1], 23,  0);
	eph->_rsvd2	= GET_FIELD_U(sf1[2], 24,  0);
	eph->_rsvd3	= GET_FIELD_U(sf1[3], 24,  0);
	eph->_rsvd4	= GET_FIELD_U(sf1[4], 16,  8);
	eph->aodo	= GET_FIELD_U(sf2[7],  5,  2);

	/* Check & cross-validate iodc[7:0], iode1, iode2 */
	if ((iode1 != iode2) || (iode1 != (eph->iodc & 0xff)))
		return -1;

	return 0;
}


/*
 * Unpacks GPS Subframe 4 or 5 Almanac pages payload (8 words)
 *
 * (no parity bit checking is done, only the lower 24 bits of each word
 *  are used)
 */
int
gps_unpack_sf45_almanac(uint32_t *sf, struct gps_almanac_sv *alm)
{
	alm->sv_id      = GET_FIELD_U(sf[0],  6, 16);

	alm->e		= GET_FIELD_U(sf[0], 16,  0);
	alm->t_oa	= GET_FIELD_U(sf[1],  8, 16);
	alm->ksii	= GET_FIELD_S(sf[1], 16,  0);
	alm->omega_dot	= GET_FIELD_S(sf[2], 16,  8);
	alm->sv_health	= GET_FIELD_U(sf[2],  8,  0);
	alm->a_powhalf	= GET_FIELD_U(sf[3], 24,  0);
	alm->omega_0	= GET_FIELD_S(sf[4], 24,  0);
	alm->w		= GET_FIELD_S(sf[5], 24,  0);
	alm->m_0	= GET_FIELD_S(sf[6], 24,  0);
	alm->a_f0	= (GET_FIELD_S(sf[7], 8, 16) << 3) | \
	                   GET_FIELD_U(sf[7], 3,  2);
	alm->a_f1	= GET_FIELD_S(sf[7], 11,  5);

	return 0;
}