source: ntrip/trunk/BNC/RTCM/RTCM2.cpp@ 709

Last change on this file since 709 was 709, checked in by weber, 16 years ago

* empty log message *

File size: 31.2 KB
Line 
1//------------------------------------------------------------------------------
2//
3// RTCM2.cpp
4//
5// Purpose:
6//
7// Module for extraction of RTCM2 messages
8//
9// References:
10//
11// RTCM 10402.3 Recommended Standards for Differential GNSS (Global
12// Navigation Satellite Systems) Service; RTCM Paper 136-2001/SC104-STD,
13// Version 2.3, 20 Aug. 2001; Radio Technical Commission For Maritime
14// Services, Alexandria, Virgina (2001).
15// ICD-GPS-200; Navstar GPS Space Segment / Navigation User Interfaces;
16// Revison C; 25 Sept. 1997; Arinc Research Corp., El Segundo (1997).
17// Jensen M.; RTCM2ASC Documentation;
18// URL http://kom.aau.dk/~borre/masters/receiver/rtcm2asc.htm;
19// last accessed 17 Sep. 2006
20// Sager J.; Decoder for RTCM SC-104 data from a DGPS beacon receiver;
21// URL http://www.wsrcc.com/wolfgang/ftp/rtcm-0.3.tar.gz;
22// last accessed 17 Sep. 2006
23//
24// Notes:
25//
26// - The host computer is assumed to use little endian (Intel) byte order
27//
28// Last modified:
29//
30// 2006/09/17 OMO Created
31// 2006/09/19 OMO Fixed getHeader() methods
32// 2006/09/21 OMO Reduced phase ambiguity to 2^23 cycles
33// 2006/10/05 OMO Specified const'ness of various member functions
34// 2006/10/13 LMV Fixed resolvedPhase to handle missing C1 range
35// 2006/10/14 LMV Fixed loop cunter in ThirtyBitWord
36// 2006/10/14 LMV Exception handling
37// 2006/10/17 OMO Removed obsolete check of multiple message indicator
38// 2006/10/17 OMO Fixed parity handling
39// 2006/10/18 OMO Improved screening of bad data in RTCM2_Obs::extract
40// 2006/11/25 OMO Revised check for presence of GLONASS data
41// 2007/05/25 GW Round time tag to 100 ms
42// 2007/12/11 AHA Changed handling of C/A- and P-Code on L1
43// 2007/12/13 AHA Changed epoch comparison in packet extraction
44// 2008/03/01 OMO Compilation flag for epoch rounding
45// 2008/03/04 AHA Fixed problems with PRN 32
46// 2008/03/05 AHA Implemeted fix for Trimble 4000SSI receivers
47// 2008/03/07 AHA Major revision of input buffer handling
48// 2008/03/07 AHA Removed unnecessary failure flag
49//
50// (c) DLR/GSOC
51//
52//------------------------------------------------------------------------------
53
54#include <bitset>
55#include <cmath>
56#include <fstream>
57#include <iomanip>
58#include <iostream>
59#include <string>
60#include <vector>
61
62#include "RTCM2.h"
63
64// Activate (1) or deactivate (0) debug output for tracing parity errors and
65// undersized packets in get(Unsigned)Bits
66
67#define DEBUG 0
68
69// Activate (1) or deactivate (0) rounding of measurement epochs to 100ms
70//
71// Note: A need to round the measurement epoch to integer tenths of a second was
72// noted by BKG in the processing of RTCM2 data from various receivers in NTRIP
73// real-time networks. It is unclear at present, whether this is due to an
74// improper implementation of the RTCM2 standard in the respective receivers
75// or an unclear formulation of the standard.
76
77#define ROUND_EPOCH 1
78
79// Fix for data streams originating from TRIMBLE_4000SSI receivers.
80// GPS PRN32 is erroneously flagged as GLONASS satellite in the C/A
81// pseudorange messages. We therefore use a majority voting to
82// determine the true constellation for this message.
83// This fix is only required for Trimble4000SSI receivers but can also
84// be used with all other known receivers.
85
86#define FIX_TRIMBLE_4000SSI 1
87
88using namespace std;
89
90
91// GPS constants
92
93const double c_light = 299792458.0; // Speed of light [m/s]; IAU 1976
94const double f_L1 = 1575.42e6; // L1 frequency [Hz] (10.23MHz*154)
95const double f_L2 = 1227.60e6; // L2 frequency [Hz] (10.23MHz*120)
96
97const double lambda_L1 = c_light/f_L1; // L1 wavelength [m] (0.1903m)
98const double lambda_L2 = c_light/f_L2; // L2 wavelength [m]
99
100//
101// Bits for message availability checks
102//
103
104const int bit_L1rngGPS = 0;
105const int bit_L2rngGPS = 1;
106const int bit_L1cphGPS = 2;
107const int bit_L2cphGPS = 3;
108const int bit_L1rngGLO = 4;
109const int bit_L2rngGLO = 5;
110const int bit_L1cphGLO = 6;
111const int bit_L2cphGLO = 7;
112
113
114//
115// namespace rtcm2
116//
117
118namespace rtcm2 {
119
120
121//------------------------------------------------------------------------------
122//
123// class ThirtyBitWord (implementation)
124//
125// Purpose:
126//
127// Handling of RTCM2 30bit words
128//
129//------------------------------------------------------------------------------
130
131// Constructor
132
133ThirtyBitWord::ThirtyBitWord() : W(0) {
134};
135
136// Clear entire 30-bit word and 2-bit parity from previous word
137
138void ThirtyBitWord::clear() {
139 W = 0;
140};
141
142// Parity check
143
144bool ThirtyBitWord::validParity() const {
145
146 // Parity stuff
147
148 static const unsigned int PARITY_25 = 0xBB1F3480;
149 static const unsigned int PARITY_26 = 0x5D8F9A40;
150 static const unsigned int PARITY_27 = 0xAEC7CD00;
151 static const unsigned int PARITY_28 = 0x5763E680;
152 static const unsigned int PARITY_29 = 0x6BB1F340;
153 static const unsigned int PARITY_30 = 0x8B7A89C0;
154
155 // Look-up table for parity of eight bit bytes
156 // (parity=0 if the number of 0s and 1s is equal, else parity=1)
157 static unsigned char byteParity[] = {
158 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
159 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
160 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
161 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
162 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
163 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
164 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
165 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0
166 };
167
168 // Local variables
169
170 unsigned int t, w, p;
171
172 // The sign of the data is determined by the D30* parity bit
173 // of the previous data word. If D30* is set, invert the data
174 // bits D01..D24 to obtain the d01..d24 (but leave all other
175 // bits untouched).
176
177 w = W;
178 if ( w & 0x40000000 ) w ^= 0x3FFFFFC0;
179
180 // Compute the parity of the sign corrected data bits d01..d24
181 // as described in the ICD-GPS-200
182
183 t = w & PARITY_25;
184 p = ( byteParity[t &0xff] ^ byteParity[(t>> 8)&0xff] ^
185 byteParity[(t>>16)&0xff] ^ byteParity[(t>>24) ] );
186
187 t = w & PARITY_26;
188 p = (p<<1) |
189 ( byteParity[t &0xff] ^ byteParity[(t>> 8)&0xff] ^
190 byteParity[(t>>16)&0xff] ^ byteParity[(t>>24) ] );
191
192 t = w & PARITY_27;
193 p = (p<<1) |
194 ( byteParity[t &0xff] ^ byteParity[(t>> 8)&0xff] ^
195 byteParity[(t>>16)&0xff] ^ byteParity[(t>>24) ] );
196
197 t = w & PARITY_28;
198 p = (p<<1) |
199 ( byteParity[t &0xff] ^ byteParity[(t>> 8)&0xff] ^
200 byteParity[(t>>16)&0xff] ^ byteParity[(t>>24) ] );
201
202 t = w & PARITY_29;
203 p = (p<<1) |
204 ( byteParity[t &0xff] ^ byteParity[(t>> 8)&0xff] ^
205 byteParity[(t>>16)&0xff] ^ byteParity[(t>>24) ] );
206
207 t = w & PARITY_30;
208 p = (p<<1) |
209 ( byteParity[t &0xff] ^ byteParity[(t>> 8)&0xff] ^
210 byteParity[(t>>16)&0xff] ^ byteParity[(t>>24) ] );
211
212 return ( (W & 0x3f) == p);
213
214};
215
216
217// Check preamble
218
219bool ThirtyBitWord::isHeader() const {
220
221 const unsigned char Preamble = 0x66;
222
223 unsigned char b = (value()>>22) & 0xFF;
224
225 return ( b==Preamble );
226
227};
228
229
230// Return entire 32-bit (current word and previous parity)
231
232unsigned int ThirtyBitWord::all() const {
233 return W;
234};
235
236
237// Return sign-corrected 30-bit (or zero if parity mismatch)
238
239unsigned int ThirtyBitWord::value() const {
240
241 unsigned int w = W;
242
243 if (validParity()) {
244 // Return data and current parity bits. Invert data bits if D30*
245 // is set and discard old parity bits.
246 if ( w & 0x40000000 ) w ^= 0x3FFFFFC0;
247 return (w & 0x3FFFFFFF);
248 }
249 else {
250 // Error; invalid parity
251 return 0;
252 };
253
254};
255
256
257// Append a byte with six data bits
258
259void ThirtyBitWord::append(unsigned char b) {
260
261 // Look up table for swap (left-right) of 6 data bits
262 static const unsigned char
263 swap[] = {
264 0,32,16,48, 8,40,24,56, 4,36,20,52,12,44,28,60,
265 2,34,18,50,10,42,26,58, 6,38,22,54,14,46,30,62,
266 1,33,17,49, 9,41,25,57, 5,37,21,53,13,45,29,61,
267 3,35,19,51,11,43,27,59, 7,39,23,55,15,47,31,63
268 };
269
270 // Bits 7 and 6 (of 0..7) must be "01" for valid data bytes
271 if ( (b & 0x40) != 0x40 ) {
272 // We simply skip the invalid input byte and leave the word unchanged
273 return;
274 };
275
276 // Swap bits 0..5 to restore proper bit order for 30bit words
277 b = swap[ b & 0x3f];
278
279 // Fill word
280 W = ( (W <<6) | (b & 0x3f) ) ;
281
282};
283
284
285// Get next 30bit word from string
286
287void ThirtyBitWord::get(const string& buf) {
288
289 // Check if string is long enough
290
291 if (buf.size()<5) {
292 // Ignore; users should avoid this case prior to calling get()
293 return;
294 };
295
296 // Process 5 bytes and remove them from the input
297
298 for (int i=0; i<5; i++) append(buf[i]);
299
300#if (DEBUG>0)
301 if (!validParity()) {
302 cerr << "Parity error in get()"
303 << bitset<32>(all()) << endl;
304 };
305#endif
306
307};
308
309// Get next 30bit word from file
310
311void ThirtyBitWord::get(istream& inp) {
312
313 unsigned char b;
314
315 for (int i=0; i<5; i++) {
316 inp >> b;
317 if (inp.fail()) { clear(); return; };
318 append(b);
319 };
320
321#if (DEBUG>0)
322 if (!validParity()) {
323 cerr << "Parity error in get()"
324 << bitset<32>(all()) << endl;
325 };
326#endif
327
328};
329
330// Get next header word from string
331
332void ThirtyBitWord::getHeader(string& buf) {
333
334 const int wordLen = 5; // Number of bytes representing a 30-bit word
335 const int spare = 1; // Number of spare words for resync of parity
336 // (same value as inRTCM2packet::getPacket())
337 unsigned int i;
338
339 i=0;
340 while (!isHeader() && i<buf.size() ) {
341 // Process byte
342 append(buf[i]);
343 // Increment count
344 i++;
345 };
346
347 // Remove processed bytes from buffer. Retain also the previous word to
348 // allow a resync if getHeader() is called repeatedly on the same buffer.
349 if (i>=(1+spare)*wordLen) buf.erase(0,i-(1+spare)*wordLen);
350
351#if (DEBUG>0)
352 if (!validParity()) {
353 cerr << "Parity error in getHeader()"
354 << bitset<32>(all()) << endl;
355 };
356#endif
357
358};
359
360// Get next header word from file
361
362void ThirtyBitWord::getHeader(istream& inp) {
363
364 unsigned char b;
365 unsigned int i;
366
367 i=0;
368 while ( !isHeader() || i<5 ) {
369 inp >> b;
370 if (inp.fail()) { clear(); return; };
371 append(b); i++;
372 };
373
374#if (DEBUG>0)
375 if (!validParity()) {
376 cerr << "Parity error in getHeader()"
377 << bitset<32>(all()) << endl;
378 };
379#endif
380
381};
382
383
384//------------------------------------------------------------------------------
385//
386// RTCM2packet (class implementation)
387//
388// Purpose:
389//
390// A class for handling RTCM2 data packets
391//
392//------------------------------------------------------------------------------
393
394// Constructor
395
396RTCM2packet::RTCM2packet() {
397 clear();
398};
399
400// Initialization
401
402void RTCM2packet::clear() {
403
404 W.clear();
405
406 H1=0;
407 H2=0;
408
409 DW.resize(0,0);
410
411};
412
413// Complete packet, valid parity
414
415bool RTCM2packet::valid() const {
416
417 // The methods for creating a packet (get,">>") ensure
418 // that a packet has a consistent number of data words
419 // and a valid parity in all header and data words.
420 // Therefore a packet is either empty or valid.
421
422 return (H1!=0);
423
424};
425
426
427//
428// Gets the next packet from the buffer
429//
430
431void RTCM2packet::getPacket(std::string& buf) {
432
433 const int wordLen = 5; // Number of bytes representing a 30-bit word
434 const int spare = 1; // Number of spare words for resync of parity
435 // (same value as used in ThirtyBitWord::getHeader)
436 unsigned int n;
437
438 // Try to read a full packet. Processed bytes are removed from the input
439 // buffer except for the latest spare*wordLen bytes to restore the parity
440 // bytes upon subseqeunt calls of getPAcket().
441
442 // Locate and read the first header word
443 W.getHeader(buf);
444 if (!W.isHeader()) {
445 // No header found; try again next time. buf retains only the spare
446 // words. The packet contents is cleared to indicate an unsuccessful
447 // termination of getPacket().
448 clear();
449 return;
450 };
451 H1 = W.value();
452
453 // Do we have enough bytes to read the next word? If not, the packet
454 // contents is cleared to indicate an unsuccessful termination. The
455 // previously read spare and header bytes are retained in the buffer
456 // for use in the next call of getPacket().
457 if (buf.size()<(spare+2)*wordLen) { clear(); return; };
458
459 // Read the second header word
460 W.get(buf.substr((spare+1)*wordLen,buf.size()-1-(spare+1)*wordLen));
461 H2 = W.value();
462 if (!W.validParity()) {
463 // Invalid H2 word; delete first buffer byte and try to resynch next time.
464 // The packet contents is cleared to indicate an unsuccessful termination.
465 clear();
466 buf.erase(0,1);
467 return;
468 };
469
470 n = nDataWords();
471
472 // Do we have enough bytes to read the next word? If not, the packet
473 // contents is cleared to indicate an unsuccessful termination. The
474 // previously read spare and header bytes are retained in the buffer
475 // for use in the next call of getPacket().
476 if (buf.size()<(spare+2+n)*wordLen) { clear(); return; };
477
478 DW.resize(n);
479 for (unsigned int i=0; i<n; i++) {
480 W.get(buf.substr((spare+2+i)*wordLen,buf.size()-1-(spare+2+i)*wordLen));
481 DW[i] = W.value();
482 if (!W.validParity()) {
483 // Invalid data word; delete first byte and try to resynch next time.
484 // The packet contents is cleared to indicate an unsuccessful termination.
485 clear();
486 buf.erase(0,1);
487 return;
488 };
489 };
490
491 // Successful packet extraction; delete total number of message bytes
492 // from buffer.
493 // Note: a total of "spare" words remain in the buffer to enable a
494 // parity resynchronization when searching the next header.
495
496 buf.erase(0,(n+2)*wordLen);
497
498 return;
499
500};
501
502
503//
504// Gets the next packet from the input stream
505//
506
507void RTCM2packet::getPacket(std::istream& inp) {
508
509 int n;
510
511 W.getHeader(inp);
512 H1 = W.value();
513 if (inp.fail() || !W.isHeader()) { clear(); return; }
514
515 W.get(inp);
516 H2 = W.value();
517 if (inp.fail() || !W.validParity()) { clear(); return; }
518
519 n = nDataWords();
520 DW.resize(n);
521 for (int i=0; i<n; i++) {
522 W.get(inp);
523 DW[i] = W.value();
524 if (inp.fail() || !W.validParity()) { clear(); return; }
525 };
526
527 return;
528
529};
530
531//
532// Input operator
533//
534// Reads an RTCM2 packet from the input stream.
535//
536
537istream& operator >> (istream& is, RTCM2packet& p) {
538
539 p.getPacket(is);
540
541 return is;
542
543};
544
545// Access methods
546
547unsigned int RTCM2packet::header1() const {
548 return H1;
549};
550
551unsigned int RTCM2packet::header2() const {
552 return H2;
553};
554
555unsigned int RTCM2packet::dataWord(int i) const {
556 if ( (unsigned int)i < DW.size() ) {
557 return DW[i];
558 }
559 else {
560 return 0;
561 }
562};
563
564unsigned int RTCM2packet::msgType() const {
565 return ( H1>>16 & 0x003F );
566};
567
568unsigned int RTCM2packet::stationID() const {
569 return ( H1>> 6 & 0x03FF );
570};
571
572unsigned int RTCM2packet::modZCount() const {
573 return ( H2>>17 & 0x01FFF );
574};
575
576unsigned int RTCM2packet::seqNumber() const {
577 return ( H2>>14 & 0x0007 );
578};
579
580unsigned int RTCM2packet::nDataWords() const {
581 return ( H2>> 9 & 0x001F );
582};
583
584unsigned int RTCM2packet::staHealth() const {
585 return ( H2>> 6 & 0x0003 );
586};
587
588
589//
590// Get unsigned bit field
591//
592// Bits are numbered from left (msb) to right (lsb) starting at bit 0
593//
594
595unsigned int RTCM2packet::getUnsignedBits ( unsigned int start,
596 unsigned int n ) const {
597
598 unsigned int iFirst = start/24; // Index of first data word
599 unsigned int iLast = (start+n-1)/24; // Index of last data word
600 unsigned int bitField = 0;
601 unsigned int tmp;
602
603 // Checks
604
605 if (n>32) {
606 throw("Error: can't handle >32 bits in RTCM2packet::getUnsignedBits");
607 };
608
609 if ( 24*DW.size() < start+n-1 ) {
610#if (DEBUG>0)
611 cerr << "Debug output RTCM2packet::getUnsignedBits" << endl
612 << " P.msgType: " << setw(5) << msgType() << endl
613 << " P.nDataWords: " << setw(5) << nDataWords() << endl
614 << " start: " << setw(5) << start << endl
615 << " n: " << setw(5) << n << endl
616 << " P.H1: " << setw(5) << bitset<32>(H1) << endl
617 << " P.H2: " << setw(5) << bitset<32>(H2) << endl
618 << endl
619 << flush;
620#endif
621 throw("Error: Packet too short in RTCM2packet::getUnsignedBits");
622 }
623
624 // Handle initial data word
625 // Get all data bits. Strip parity and unwanted leading bits.
626 // Store result in 24 lsb bits of tmp.
627
628 tmp = (DW[iFirst]>>6) & 0xFFFFFF;
629 tmp = ( ( tmp << start%24) & 0xFFFFFF ) >> start%24 ;
630
631 // Handle central data word
632
633 if ( iFirst<iLast ) {
634 bitField = tmp;
635 for (unsigned int iWord=iFirst+1; iWord<iLast; iWord++) {
636 tmp = (DW[iWord]>>6) & 0xFFFFFF;
637 bitField = (bitField << 24) | tmp;
638 };
639 tmp = (DW[iLast]>>6) & 0xFFFFFF;
640 };
641
642 // Handle last data word
643
644 tmp = tmp >> (23-(start+n-1)%24);
645 bitField = (bitField << ((start+n-1)%24+1)) | tmp;
646
647 // Done
648
649 return bitField;
650
651};
652
653//
654// Get signed bit field
655//
656// Bits are numbered from left (msb) to right (lsb) starting at bit 0
657//
658
659int RTCM2packet::getBits ( unsigned int start,
660 unsigned int n ) const {
661
662
663 // Checks
664
665 if (n>32) {
666 throw("Error: can't handle >32 bits in RTCM2packet::getBits");
667 };
668
669 if ( 24*DW.size() < start+n-1 ) {
670#if (DEBUG>0)
671 cerr << "Debug output RTCM2packet::getUnsignedBits" << endl
672 << " P.msgType: " << setw(5) << msgType() << endl
673 << " P.nDataWords: " << setw(5) << nDataWords() << endl
674 << " start: " << setw(5) << start << endl
675 << " n: " << setw(5) << n << endl
676 << " P.H1: " << setw(5) << bitset<32>(H1) << endl
677 << " P.H2: " << setw(5) << bitset<32>(H2) << endl
678 << endl
679 << flush;
680#endif
681 throw("Error: Packet too short in RTCM2packet::getBits");
682 }
683
684 return ((int)(getUnsignedBits(start,n)<<(32-n))>>(32-n));
685
686};
687
688
689//------------------------------------------------------------------------------
690//
691// RTCM2_03 (class implementation)
692//
693// Purpose:
694//
695// A class for handling RTCM 2 GPS Reference Station Parameters messages
696//
697//------------------------------------------------------------------------------
698
699void RTCM2_03::extract(const RTCM2packet& P) {
700
701 // Check validity and packet type
702
703 validMsg = (P.valid());
704 if (!validMsg) return;
705
706 validMsg = (P.ID()==03);
707 if (!validMsg) return;
708
709 // Antenna reference point coordinates
710
711 x = P.getBits( 0,32)*0.01; // X [m]
712 y = P.getBits(32,32)*0.01; // Y [m]
713 z = P.getBits(64,32)*0.01; // Z [m]
714
715};
716
717//------------------------------------------------------------------------------
718//
719// RTCM2_23 (class implementation)
720//
721// Purpose:
722//
723// A class for handling RTCM 2 Antenna Type Definition messages
724//
725//------------------------------------------------------------------------------
726
727void RTCM2_23::extract(const RTCM2packet& P) {
728
729 int nad, nas;
730
731 // Check validity and packet type
732
733 validMsg = (P.valid());
734 if (!validMsg) return;
735
736 validMsg = (P.ID()==23);
737 if (!validMsg) return;
738
739 // Antenna descriptor
740 antType = "";
741 nad = P.getUnsignedBits(3,5);
742 for (int i=0;i<nad;i++)
743 antType += (char)P.getUnsignedBits(8+i*8,8);
744
745 // Optional antenna serial numbers
746 if (P.getUnsignedBits(2,1)==1) {
747 nas = P.getUnsignedBits(19+8*nad,5);
748 antSN = "";
749 for (int i=0;i<nas;i++)
750 antSN += (char)P.getUnsignedBits(24+8*nas+i*8,8);
751 };
752
753};
754
755
756//------------------------------------------------------------------------------
757//
758// RTCM2_24 (class implementation)
759//
760// Purpose:
761//
762// A class for handling RTCM 2 Reference Station Antenna
763// Reference Point Parameter messages
764//
765//------------------------------------------------------------------------------
766
767void RTCM2_24::extract(const RTCM2packet& P) {
768
769 double dx,dy,dz;
770
771 // Check validity and packet type
772
773 validMsg = (P.valid());
774 if (!validMsg) return;
775
776 validMsg = (P.ID()==24);
777 if (!validMsg) return;
778
779 // System indicator
780
781 isGPS = (P.getUnsignedBits(118,1)==0);
782 isGLONASS = (P.getUnsignedBits(118,1)==1);
783
784 // Antenna reference point coordinates
785
786 x = 64.0*P.getBits( 0,32);
787 y = 64.0*P.getBits(40,32);
788 z = 64.0*P.getBits(80,32);
789 dx = P.getUnsignedBits( 32,6);
790 dy = P.getUnsignedBits( 72,6);
791 dz = P.getUnsignedBits(112,6);
792 x = 0.0001*( x + (x<0? -dx:+dx) );
793 y = 0.0001*( y + (y<0? -dy:+dy) );
794 z = 0.0001*( z + (z<0? -dz:+dz) );
795
796 // Antenna Height
797
798 if (P.getUnsignedBits(119,1)==1) {
799 h= P.getUnsignedBits(120,18)*0.0001;
800 };
801
802
803};
804
805
806//------------------------------------------------------------------------------
807//
808// RTCM2_Obs (class definition)
809//
810// Purpose:
811//
812// A class for handling blocks of RTCM2 18 & 19 packets that need to be
813// combined to get a complete set of measurements
814//
815// Notes:
816//
817// The class collects L1/L2 code and phase measurements for GPS and GLONASS.
818// Since the Multiple Message Indicator is inconsistently handled by various
819// receivers we simply require code and phase on L1 and L2 for a complete
820// set ob observations at a given epoch. GLONASS observations are optional,
821// but all four types (code+phase,L1+L2) must be provided, if at least one
822// is given. Also, the GLONASS message must follow the corresponding GPS
823// message.
824//
825//------------------------------------------------------------------------------
826
827// Constructor
828
829RTCM2_Obs::RTCM2_Obs() {
830
831 clear();
832 GPSonly = true;
833
834};
835
836// Reset entire block
837
838void RTCM2_Obs::clear() {
839
840 secs=0.0; // Seconds of hour (GPS time)
841 nSat=0; // Number of space vehicles
842 PRN.resize(0); // Pseudorange [m]
843 rng_C1.resize(0); // Pseudorange [m]
844 rng_P1.resize(0); // Pseudorange [m]
845 rng_P2.resize(0); // Pseudorange [m]
846 cph_L1.resize(0); // Carrier phase [m]
847 cph_L2.resize(0); // Carrier phase [m]
848
849 availability.reset(); // Message status flags
850
851};
852
853// Availability checks
854
855bool RTCM2_Obs::anyGPS() const {
856
857 return availability.test(bit_L1rngGPS) ||
858 availability.test(bit_L2rngGPS) ||
859 availability.test(bit_L1cphGPS) ||
860 availability.test(bit_L2cphGPS);
861
862};
863
864bool RTCM2_Obs::anyGLONASS() const {
865
866 return availability.test(bit_L1rngGLO) ||
867 availability.test(bit_L2rngGLO) ||
868 availability.test(bit_L1cphGLO) ||
869 availability.test(bit_L2cphGLO);
870
871};
872
873bool RTCM2_Obs::allGPS() const {
874
875 return availability.test(bit_L1rngGPS) &&
876 availability.test(bit_L2rngGPS) &&
877 availability.test(bit_L1cphGPS) &&
878 availability.test(bit_L2cphGPS);
879
880};
881
882bool RTCM2_Obs::allGLONASS() const {
883
884 return availability.test(bit_L1rngGLO) &&
885 availability.test(bit_L2rngGLO) &&
886 availability.test(bit_L1cphGLO) &&
887 availability.test(bit_L2cphGLO);
888
889};
890
891// Validity
892
893bool RTCM2_Obs::valid() const {
894
895 return ( allGPS() && ( GPSonly || allGLONASS() ) );
896
897};
898
899
900//
901// Extract RTCM2 18 & 19 messages and store relevant data for future use
902//
903
904void RTCM2_Obs::extract(const RTCM2packet& P) {
905
906 bool isGPS,isCAcode,isL1,isOth;
907 int NSat,idx;
908 int sid,prn;
909 double t,rng,cph;
910
911 // Check validity and packet type
912
913 if ( ! ( P.valid() &&
914 (P.ID()==18 || P.ID()==19) &&
915 P.nDataWords()>1 ) ) return;
916
917 // Clear previous data if block was already complete
918
919 if (valid()) clear();
920
921 // Process carrier phase message
922
923 if ( P.ID()==18 ) {
924
925 // Number of satellites in current message
926 NSat = (P.nDataWords()-1)/2;
927
928 // Current epoch (mod 3600 sec)
929 t = 0.6*P.modZCount()
930 + P.getUnsignedBits(4,20)*1.0e-6;
931
932#if (ROUND_EPOCH==1)
933 // SC-104 V2.3 4-42 Note 1 4. Assume measurements at hard edges
934 // of receiver clock with minimum divisions of 10ms
935 // and clock error less then recommended 1.1ms
936 // Hence, round time tag to 100 ms
937 t = floor(t*100.0+0.5)/100.0;
938#endif
939
940 // Frequency (exit if neither L1 nor L2)
941 isL1 = ( P.getUnsignedBits(0,1)==0 );
942 isOth = ( P.getUnsignedBits(1,1)==1 );
943 if (isOth) return;
944
945 // Constellation (for first satellite in message)
946 isGPS = ( P.getUnsignedBits(26,1)==0 );
947 GPSonly = GPSonly && isGPS;
948
949 // Multiple Message Indicator (only checked for first satellite)
950 // pendingMsg = ( P.getUnsignedBits(24,1)==1 );
951
952 // Handle epoch: store epoch of first GPS message and
953 // check consistency of subsequent messages. GLONASS time tags
954 // are different and have to be ignored
955 if (isGPS) {
956 if ( nSat==0 ) {
957 secs = t; // Store epoch
958 }
959// else if (t!=secs) {
960 else if (abs(t-secs)>1e-6) {
961 clear(); secs = t; // Clear all data, then store epoch
962 };
963 };
964
965 // Discard GLONASS observations if no prior GPS observations
966 // are available
967 if (!isGPS && !anyGPS() ) return;
968
969 // Set availability flags
970
971 if ( isL1 && isGPS) availability.set(bit_L1cphGPS);
972 if (!isL1 && isGPS) availability.set(bit_L2cphGPS);
973 if ( isL1 && !isGPS) availability.set(bit_L1cphGLO);
974 if (!isL1 && !isGPS) availability.set(bit_L2cphGLO);
975
976 // Process all satellites
977
978 for (int iSat=0;iSat<NSat;iSat++){
979
980 // Code type
981 isCAcode = ( P.getUnsignedBits(iSat*48+25,1)==0 );
982
983 // Satellite
984 sid = P.getUnsignedBits(iSat*48+27,5);
985 if (sid==0) sid=32;
986
987 prn = (isGPS? sid : sid+200 );
988
989 // Carrier phase measurement (mod 2^23 [cy]; sign matched to range)
990 cph = -P.getBits(iSat*48+40,32)/256.0;
991
992 // Is this a new PRN?
993 idx=-1;
994 for (unsigned int i=0;i<PRN.size();i++) {
995 if (PRN[i]==prn) { idx=i; break; };
996 };
997 if (idx==-1) {
998 // Insert new sat at end of list
999 nSat++; idx = nSat-1;
1000 PRN.push_back(prn);
1001 rng_C1.push_back(0.0);
1002 rng_P1.push_back(0.0);
1003 rng_P2.push_back(0.0);
1004 cph_L1.push_back(0.0);
1005 cph_L2.push_back(0.0);
1006 };
1007
1008 // Store measurement
1009 if (isL1) {
1010 cph_L1[idx] = cph;
1011 }
1012 else {
1013 cph_L2[idx] = cph;
1014 };
1015
1016 };
1017
1018 };
1019
1020
1021 // Process pseudorange message
1022
1023 if ( P.ID()==19 ) {
1024
1025 // Number of satellites in current message
1026 NSat = (P.nDataWords()-1)/2;
1027
1028 // Current epoch (mod 3600 sec)
1029 t = 0.6*P.modZCount()
1030 + P.getUnsignedBits(4,20)*1.0e-6;
1031
1032#if (ROUND_EPOCH==1)
1033 // SC-104 V2.3 4-42 Note 1 4. Assume measurements at hard edges
1034 // of receiver clock with minimum divisions of 10ms
1035 // and clock error less then recommended 1.1ms
1036 // Hence, round time tag to 100 ms
1037 t = floor(t*100.0+0.5)/100.0;
1038#endif
1039
1040 // Frequency (exit if neither L1 nor L2)
1041 isL1 = ( P.getUnsignedBits(0,1)==0 );
1042 isOth = ( P.getUnsignedBits(1,1)==1 );
1043 if (isOth) return;
1044
1045#if (FIX_TRIMBLE_4000SSI==1)
1046 // Fix for data streams originating from TRIMBLE_4000SSI receivers.
1047 // GPS PRN32 is erroneously flagged as GLONASS satellite in the C/A
1048 // pseudorange messages. We therefore use a majority voting to
1049 // determine the true constellation for this message.
1050 // This fix is only required for Trimble4000SSI receivers but can also
1051 // be used with all other known receivers.
1052 int nGPS=0;
1053 for(int iSat=0; iSat<NSat; iSat++){
1054 // Constellation (for each satellite in message)
1055 isGPS = ( P.getUnsignedBits(iSat*48+26,1)==0 );
1056 if(isGPS) nGPS++;
1057 };
1058 isGPS = (2*nGPS>NSat);
1059#else
1060 // Constellation (for first satellite in message)
1061 isGPS = ( P.getUnsignedBits(26,1)==0 );
1062#endif
1063 GPSonly = GPSonly && isGPS;
1064
1065 // Multiple Message Indicator (only checked for first satellite)
1066 // pendingMsg = ( P.getUnsignedBits(24,1)==1 );
1067
1068 // Handle epoch: store epoch of first GPS message and
1069 // check consistency of subsequent messages. GLONASS time tags
1070 // are different and have to be ignored
1071 if (isGPS) {
1072 if ( nSat==0 ) {
1073 secs = t; // Store epoch
1074 }
1075// else if (t!=secs) {
1076 else if (abs(t-secs)>1e-6) {
1077 clear(); secs = t; // Clear all data, then store epoch
1078 };
1079 };
1080
1081 // Discard GLONASS observations if no prior GPS observations
1082 // are available
1083 if (!isGPS && !anyGPS() ) return;
1084
1085 // Set availability flags
1086 if ( isL1 && isGPS) availability.set(bit_L1rngGPS);
1087 if (!isL1 && isGPS) availability.set(bit_L2rngGPS);
1088 if ( isL1 && !isGPS) availability.set(bit_L1rngGLO);
1089 if (!isL1 && !isGPS) availability.set(bit_L2rngGLO);
1090
1091 // Process all satellites
1092
1093 for (int iSat=0;iSat<NSat;iSat++){
1094
1095 // Code type
1096 isCAcode = ( P.getUnsignedBits(iSat*48+25,1)==0 );
1097
1098 // Satellite
1099 sid = P.getUnsignedBits(iSat*48+27,5);
1100 if (sid==0) sid=32;
1101 prn = (isGPS? sid : sid+200 );
1102
1103 // Pseudorange measurement [m]
1104 rng = P.getUnsignedBits(iSat*48+40,32)*0.02;
1105
1106 // Is this a new PRN?
1107 idx=-1;
1108 for (unsigned int i=0;i<PRN.size();i++) {
1109 if (PRN[i]==prn) { idx=i; break; };
1110 };
1111 if (idx==-1) {
1112 // Insert new sat at end of list
1113 nSat++; idx = nSat-1;
1114 PRN.push_back(prn);
1115 rng_C1.push_back(0.0);
1116 rng_P1.push_back(0.0);
1117 rng_P2.push_back(0.0);
1118 cph_L1.push_back(0.0);
1119 cph_L2.push_back(0.0);
1120 };
1121
1122 // Store measurement
1123 if (isL1) {
1124 if (isCAcode) {
1125 rng_C1[idx] = rng;
1126 }
1127 else {
1128 rng_P1[idx] = rng;
1129 }
1130 }
1131 else {
1132 rng_P2[idx] = rng;
1133 };
1134
1135 };
1136
1137 };
1138
1139};
1140
1141//
1142// Resolution of 2^24 cy carrier phase ambiguity
1143// caused by 32-bit data field restrictions
1144//
1145// Note: the RTCM standard specifies an ambiguity of +/-2^23 cy.
1146// However, numerous receivers generate data in the +/-2^22 cy range.
1147// A reduced ambiguity of 2^23 cy appears compatible with both cases.
1148//
1149
1150double RTCM2_Obs::resolvedPhase_L1(int i) const {
1151
1152//const double ambig = pow(2.0,24); // as per RTCM2 spec
1153 const double ambig = pow(2.0,23); // used by many receivers
1154
1155 double rng;
1156 double n;
1157
1158 if (!valid() || i<0 || i>nSat-1) return 0.0;
1159
1160 rng = rng_C1[i];
1161 if (rng==0.0) rng = rng_P1[i];
1162 if (rng==0.0) return 0.0;
1163
1164 n = floor( (rng/lambda_L1-cph_L1[i]) / ambig + 0.5 );
1165
1166 return cph_L1[i] + n*ambig;
1167
1168};
1169
1170double RTCM2_Obs::resolvedPhase_L2(int i) const {
1171
1172//const double ambig = pow(2.0,24); // as per RTCM2 spec
1173 const double ambig = pow(2.0,23); // used by many receivers
1174
1175 double rng;
1176 double n;
1177
1178 if (!valid() || i<0 || i>nSat-1) return 0.0;
1179
1180 rng = rng_C1[i];
1181 if (rng==0.0) rng = rng_P1[i];
1182 if (rng==0.0) return 0.0;
1183
1184 n = floor( (rng/lambda_L2-cph_L2[i]) / ambig + 0.5 );
1185
1186 return cph_L2[i] + n*ambig;
1187
1188};
1189
1190//
1191// Resolution of epoch using reference date (GPS week and secs)
1192//
1193
1194void RTCM2_Obs::resolveEpoch (int refWeek, double refSecs,
1195 int& epochWeek, double& epochSecs ) const {
1196
1197 const double secsPerWeek = 604800.0;
1198
1199 epochWeek = refWeek;
1200 epochSecs = secs + 3600.0*(floor((refSecs-secs)/3600.0+0.5));
1201
1202 if (epochSecs<0 ) { epochWeek--; epochSecs+=secsPerWeek; };
1203 if (epochSecs>secsPerWeek) { epochWeek++; epochSecs-=secsPerWeek; };
1204
1205};
1206
1207}; // End of namespace rtcm2
Note: See TracBrowser for help on using the repository browser.