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

Last change on this file since 366 was 366, checked in by mervart, 17 years ago

* empty log message *

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