source: ntrip/trunk/BNC/src/RTCM/RTCM2.cpp@ 8465

Last change on this file since 8465 was 7629, checked in by stuerze, 8 years ago

some value initialization in constructor is added

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