1 /*****************************************************************************
3 This is a decoder for RTCM-104 2.x, an obscure and complicated serial
4 protocol used for broadcasting pseudorange corrections from
5 differential-GPS reference stations. The applicable
8 RTCM RECOMMENDED STANDARDS FOR DIFFERENTIAL NAVSTAR GPS SERVICE,
9 RTCM PAPER 194-93/SC 104-STD
11 Ordering instructions are accessible from <http://www.rtcm.org/>
12 under "Publications". This describes version 2.1 of the RTCM specification.
13 RTCM-104 was later incrementally revised up to a 2.3 level before being
14 completely redesigned as level 3.0.
16 Also applicable is ITU-R M.823: "Technical characteristics of
17 differential transmissions for global navigation satellite systems
18 from maritime radio beacons in the frequency band 283.5 - 315 kHz in
19 region 1 and 285 - 325 kHz in regions 2 & 3."
21 The RTCM 2.x protocol uses as a transport layer the GPS satellite downlink
22 protocol described in IS-GPS-200, the Navstar GPS Interface
23 Specification. This code relies on the lower-level packet-assembly
24 code for that protocol in isgps.c.
26 The lower layer's job is done when it has assembled a message of up to
27 33 words of clean parity-checked data. At this point this upper layer
28 takes over. struct rtcm2_msg_t is overlaid on the buffer and the bitfields
29 are used to extract pieces of it. Those pieces are copied and (where
30 necessary) reassembled into a struct rtcm2_t.
32 This code and the contents of isgps.c are evolved from code by Wolfgang
33 Rupprecht. Wolfgang's decoder was loosely based on one written by
34 John Sager in 1999 (in particular the dump function emits a close
35 descendant of Sager's dump format). Here are John Sager's original
38 The RTCM decoder prints a legible representation of the input data.
39 The RTCM SC-104 specification is copyrighted, so I cannot
40 quote it - in fact, I have never read it! Most of the information
41 used to develop the decoder came from publication ITU-R M.823.
42 This is a specification of the data transmitted from LF DGPS
43 beacons in the 300kHz band. M.823 contains most of those parts of
44 RTCM SC-104 directly relevant to the air interface (there
45 are one or two annoying and vital omissions!). Information
46 about the serial interface format was gleaned from studying
47 the output of a beacon receiver test program made available on
50 This file is Copyright (c) 2010 by the GPSD project
51 BSD terms apply: see the file COPYING in the distribution root for details.
53 *****************************************************************************/
55 #include <sys/types.h>
58 #endif /* S_SPLINT_S */
63 #include <math.h> /* for round() */
66 #include "driver_rtcm2.h"
68 #ifdef RTCM104V2_ENABLE
70 #define PREAMBLE_PATTERN 0x66
72 static unsigned int tx_speed[] = { 25, 50, 100, 110, 150, 200, 250, 300 };
74 #define DIMENSION(a) (unsigned)(sizeof(a)/sizeof(a[0]))
76 void rtcm2_unpack( /*@out@*/ struct rtcm2_t *tp, char *buf)
77 /* break out the raw bits into the content fields */
81 struct rtcm2_msg_t *msg = (struct rtcm2_msg_t *)buf;
83 tp->type = msg->w1.msgtype;
84 tp->length = msg->w2.frmlen;
85 tp->zcount = msg->w2.zcnt * ZCOUNT_SCALE;
86 tp->refstaid = msg->w1.refstaid;
87 tp->seqnum = msg->w2.sqnum;
88 tp->stathlth = msg->w2.stathlth;
90 len = (int)tp->length;
96 struct b_correction_t *m = &msg->msg_type.type1.corrections[0];
100 tp->ranges.sat[n].ident = m->w3.satident1;
101 tp->ranges.sat[n].udre = m->w3.udre1;
102 tp->ranges.sat[n].issuedata = m->w4.issuedata1;
103 tp->ranges.sat[n].rangerr = m->w3.pc1 *
104 (m->w3.scale1 ? PCLARGE : PCSMALL);
105 tp->ranges.sat[n].rangerate = m->w4.rangerate1 *
106 (m->w3.scale1 ? RRLARGE : RRSMALL);
110 tp->ranges.sat[n].ident = m->w4.satident2;
111 tp->ranges.sat[n].udre = m->w4.udre2;
112 tp->ranges.sat[n].issuedata = m->w6.issuedata2;
113 tp->ranges.sat[n].rangerr = m->w5.pc2 *
114 (m->w4.scale2 ? PCLARGE : PCSMALL);
115 tp->ranges.sat[n].rangerate = m->w5.rangerate2 *
116 (m->w4.scale2 ? RRLARGE : RRSMALL);
120 tp->ranges.sat[n].ident = m->w6.satident3;
121 tp->ranges.sat[n].udre = m->w6.udre3;
122 tp->ranges.sat[n].issuedata = m->w7.issuedata3;
123 /*@ -shiftimplementation @*/
124 tp->ranges.sat[n].rangerr =
125 ((m->w6.pc3_h << 8) | (m->w7.pc3_l)) *
126 (m->w6.scale3 ? PCLARGE : PCSMALL);
127 tp->ranges.sat[n].rangerate =
128 m->w7.rangerate3 * (m->w6.scale3 ? RRLARGE : RRSMALL);
129 /*@ +shiftimplementation @*/
135 tp->ranges.nentries = n;
140 struct rtcm2_msg3 *m = &msg->msg_type.type3;
142 if ((tp->ecef.valid = len >= 4)) {
143 tp->ecef.x = ((m->w3.x_h << 8) | (m->w4.x_l)) * XYZ_SCALE;
144 tp->ecef.y = ((m->w4.y_h << 16) | (m->w5.y_l)) * XYZ_SCALE;
145 tp->ecef.z = ((m->w5.z_h << 24) | (m->w6.z_l)) * XYZ_SCALE;
150 if ((tp->reference.valid = len >= 2)) {
151 struct rtcm2_msg4 *m = &msg->msg_type.type4;
153 tp->reference.system =
154 (m->w3.dgnss == 0) ? NAVSYSTEM_GPS :
155 ((m->w3.dgnss == 1) ? NAVSYSTEM_GLONASS : NAVSYSTEM_UNKNOWN);
156 tp->reference.sense =
157 (m->w3.dat != 0) ? SENSE_GLOBAL : SENSE_LOCAL;
158 if (m->w3.datum_alpha_char1) {
159 tp->reference.datum[n++] = (char)(m->w3.datum_alpha_char1);
161 if (m->w3.datum_alpha_char2) {
162 tp->reference.datum[n++] = (char)(m->w3.datum_alpha_char2);
164 if (m->w4.datum_sub_div_char1) {
165 tp->reference.datum[n++] = (char)(m->w4.datum_sub_div_char1);
167 if (m->w4.datum_sub_div_char2) {
168 tp->reference.datum[n++] = (char)(m->w4.datum_sub_div_char2);
170 if (m->w4.datum_sub_div_char3) {
171 tp->reference.datum[n++] = (char)(m->w4.datum_sub_div_char3);
173 tp->reference.datum[n++] = '\0';
175 tp->reference.dx = m->w5.dx * DXYZ_SCALE;
177 ((m->w5.dy_h << 8) | m->w6.dy_l) * DXYZ_SCALE;
178 tp->reference.dz = m->w6.dz * DXYZ_SCALE;
180 tp->reference.sense = SENSE_INVALID;
184 for (n = 0; n < (unsigned)len; n++) {
185 struct consat_t *csp = &tp->conhealth.sat[n];
186 struct b_health_t *m = &msg->msg_type.type5.health[n];
188 csp->ident = m->sat_id;
189 csp->iodl = m->issue_of_data_link != 0;
190 csp->health = m->data_health;
192 csp->snr = (int)(m->cn0 ? (m->cn0 + CNR_OFFSET) : SNR_BAD);
194 csp->health_en = m->health_enable != 0;
195 csp->new_data = m->new_nav_data != 0;
196 csp->los_warning = m->loss_warn != 0;
197 csp->tou = m->time_unhealthy * TU_SCALE;
199 tp->conhealth.nentries = n;
202 for (w = 0; w < (unsigned)len; w++) {
203 struct station_t *np = &tp->almanac.station[n];
204 struct b_station_t *mp = &msg->msg_type.type7.almanac[w];
206 np->latitude = mp->w3.lat * LA_SCALE;
207 /*@-shiftimplementation@*/
208 np->longitude = ((mp->w3.lon_h << 8) | mp->w4.lon_l) * LO_SCALE;
209 /*@+shiftimplementation@*/
210 np->range = mp->w4.range;
212 (((mp->w4.freq_h << 6) | mp->w5.freq_l) * FREQ_SCALE) +
214 np->health = mp->w5.health;
215 np->station_id = mp->w5.station_id,
216 np->bitrate = tx_speed[mp->w5.bit_rate];
219 tp->almanac.nentries = (unsigned)(len / 3);
223 for (w = 0; w < (unsigned)len; w++) {
224 if (!msg->msg_type.type16.txt[w].byte1) {
227 tp->message[n++] = (char)(msg->msg_type.type16.txt[w].byte1);
228 if (!msg->msg_type.type16.txt[w].byte2) {
231 tp->message[n++] = (char)(msg->msg_type.type16.txt[w].byte2);
232 if (!msg->msg_type.type16.txt[w].byte3) {
235 tp->message[n++] = (char)(msg->msg_type.type16.txt[w].byte3);
238 tp->message[n++] = '\0';
242 memcpy(tp->words, msg->msg_type.rtcm2_msgunk,
243 (RTCM2_WORDS_MAX - 2) * sizeof(isgps30bits_t));
248 static bool preamble_match(isgps30bits_t * w)
250 return (((struct rtcm2_msghw1 *)w)->preamble == PREAMBLE_PATTERN);
253 static bool length_check(struct gps_packet_t *lexer)
255 return lexer->isgps.bufindex >= 2
256 && lexer->isgps.bufindex >=
257 ((struct rtcm2_msg_t *)lexer->isgps.buf)->w2.frmlen + 2u;
260 enum isgpsstat_t rtcm2_decode(struct gps_packet_t *lexer, unsigned int c)
262 return isgps_decode(lexer,
263 preamble_match, length_check, RTCM2_WORDS_MAX, c);
266 void rtcm2_sager_dump(const struct rtcm2_t *rtcm, /*@out@*/ char buf[],
268 /* dump the contents of a parsed RTCM104 message */
272 (void)snprintf(buf, buflen, "H\t%u\t%u\t%0.1f\t%u\t%u\t%u\n",
275 rtcm->zcount, rtcm->seqnum, rtcm->length, rtcm->stathlth);
277 switch (rtcm->type) {
280 for (n = 0; n < rtcm->ranges.nentries; n++) {
281 const struct rangesat_t *rsp = &rtcm->ranges.sat[n];
282 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
283 "S\t%u\t%u\t%u\t%0.1f\t%0.3f\t%0.3f\n",
287 rtcm->zcount, rsp->rangerr, rsp->rangerate);
292 if (rtcm->ecef.valid)
293 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
294 "R\t%.2f\t%.2f\t%.2f\n",
295 rtcm->ecef.x, rtcm->ecef.y, rtcm->ecef.z);
299 if (rtcm->reference.valid)
300 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
301 "D\t%s\t%1d\t%s\t%.1f\t%.1f\t%.1f\n",
302 (rtcm->reference.system == NAVSYSTEM_GPS) ? "GPS"
303 : ((rtcm->reference.system ==
304 NAVSYSTEM_GLONASS) ? "GLONASS" : "UNKNOWN"),
305 rtcm->reference.sense, rtcm->reference.datum,
306 rtcm->reference.dx, rtcm->reference.dy,
311 for (n = 0; n < rtcm->conhealth.nentries; n++) {
312 const struct consat_t *csp = &rtcm->conhealth.sat[n];
313 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
314 "C\t%2u\t%1u\t%1u\t%2d\t%1u\t%1u\t%1u\t%2u\n",
317 (unsigned)csp->health,
319 (unsigned)csp->health_en,
320 (unsigned)csp->new_data,
321 (unsigned)csp->los_warning, csp->tou);
325 case 6: /* NOP msg */
326 (void)strlcat(buf, "N\n", buflen);
330 for (n = 0; n < rtcm->almanac.nentries; n++) {
331 const struct station_t *ssp = &rtcm->almanac.station[n];
332 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
333 "A\t%.4f\t%.4f\t%u\t%.1f\t%u\t%u\t%u\n",
338 ssp->health, ssp->station_id, ssp->bitrate);
342 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
343 "T\t\"%s\"\n", rtcm->message);
347 for (n = 0; n < rtcm->length; n++)
348 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
349 "U\t0x%08x\n", rtcm->words[n]);
353 (void)strlcat(buf, ".\n", buflen);
356 #endif /* RTCM104V2_ENABLE */