-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathGNSSdataFromOSP.cpp
More file actions
1262 lines (1224 loc) · 58.3 KB
/
GNSSdataFromOSP.cpp
File metadata and controls
1262 lines (1224 loc) · 58.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/** @file GNSSdataFromOSP.cpp
* Contains the implementation of the GNSSdataFromOSP class for OSP data collected from SiRF receivers.
*
*/
#include <stdio.h>
#include "GNSSdataFromOSP.h"
//from CommonClasses
#include "Utilities.h"
///Macro to check message payload length and to log an error message if not correct
#define CHECK_PAYLOADLEN(LENGTH, ERROR_MSG) \
if (message.payloadLen() != LENGTH) { \
plog->warning(ERROR_MSG); \
}
///Macro to check if the number of satellites in the fix is lower than required and to log an error message if true
#define CHECK_SATSREQUIRED(NSV, ERROR_MSG) \
if (NSV < minSVSfix) { \
plog->warning(ERROR_MSG); \
return false; \
}
/**Constructs a GNSSdataFromOSP object using parameters passed.
*
*@param rcv the receiver name
*@param minxfix the minimum of satellites required for a fix to be considered valid
*@param applBias when true, apply clock bias obtained by receiver to correct observables, when false, do not apply them.
*@param f the FILE pointer to the already open input OSP binary file
*@param pl a pointer to the Logger to be used to record logging messages
*/
GNSSdataFromOSP::GNSSdataFromOSP(string rcv, int minxfix, bool applBias, FILE* f, Logger * pl) {
receiver = rcv;
minSVSfix = minxfix;
applyBias = applBias;
ospFile = f;
plog = pl;
dynamicLog = false;
for (int i=0; i<MAXCHANNELS; i++)
for (int j=0; j<MAXSUBFR; j++)
subfrmCh[i][j].sv = 0;
setTblValues();
}
/**Constructs a GNSSdataFromOSP object using parameters passed, and logging data into the stderr.
*
*@param rcv the receiver name
*@param minxfix the minimum of satellites required for a fix to be considered
*@param applBias when true, apply clock bias obtained by receiver to correct observables, when false, do not apply them.
*@param f the FILE pointer to the the already open input OSP binary file
*/
GNSSdataFromOSP::GNSSdataFromOSP(string rcv, int minxfix, bool applBias, FILE* f) {
receiver = rcv;
minSVSfix = minxfix;
applyBias = applBias;
ospFile = f;
for (int i=0; i<MAXCHANNELS; i++)
for (int j=0; j<MAXSUBFR; j++)
subfrmCh[i][j].sv = 0;
plog = new Logger();
dynamicLog = true;
setTblValues();
}
/**Destroys a GNSSdataFromOSP object
*/
GNSSdataFromOSP::~GNSSdataFromOSP(void) {
if (dynamicLog) delete plog;
}
/**acqHeaderData extracts data from the binary OSP file for RINEX file header.
* The RINEX header data to be extracted from the binary file are:
* - the receiver identification contained in the first MID6 message
* - the initial X,Y,Z position contained in the first MID2 message
* - the time of first epoch contained in the first valid MID7 message
* - the measurement interval, computed as the time difference between two consecutive valid MID7
*<p>The method iterates over the input file extracting messages until above describe data are acquired
* or it reaches the end of file.
* <p>It logs at FINE level a message stating which header data have been acquired or not.
*
* @param rinex the RinexData object where data got from receiver will be placed
* @return true if all above described header data are properly extracted, false otherwise
*/
bool GNSSdataFromOSP::acqHeaderData(RinexData &rinex) {
bool rxIdSet = false; //identification of receiver not set
bool apxSet = false; //approximate position not set
bool frsEphSet = false; //epoch data not received
bool intrvBegin = false; //time for the 1st epoch (having correct time) not stated
bool intrvSet = false; //observations interval not set
int mid;
plog->info("RINEX header data acquisition:");
while (message.fill(ospFile) && //there are messages in the binary file
!(apxSet && rxIdSet && frsEphSet && intrvSet)) { //not all header data have been acquired
mid = message.get(); //get first byte (MID)
switch(mid) {
case 2: //collect first MID2 data to obtain approximate position (X, Y, Z)
if (!apxSet) apxSet = getMID2PosData(rinex);
break;
case 6: //extract the software version
if (!rxIdSet) rxIdSet = getMID6RxData(rinex);
break;
case 7: //computer time interval from two consecutive epochs with correct time data
if (frsEphSet) {
if (intrvBegin) frsEphSet = intrvBegin = intrvSet = getMID7Interval(rinex);
else {
frsEphSet = intrvBegin = getMID7TimeData(rinex);
rinex.setHdLnData(rinex.TOFO);
}
}
break;
case 28: //epoch data measurements. They precede the MID7 for the epoch
frsEphSet = true;
break;
default:
break;
}
}
//log data sources available or not
string logMessage = "Header data acquired:";
logMessage += apxSet? " Aprox. position;" : ";";
logMessage += intrvBegin? " 1st epoch time;" : ";";
logMessage += intrvSet? " Observation interval;" : ";";
logMessage += rxIdSet? " Receiver version" : "";
plog->info(logMessage);
return (apxSet && intrvBegin && rxIdSet && intrvSet);
}
/**acqHeaderData extracts data from the binary OSP file for a RTK file header.
* The RTK header data to be extracted from the binary file are:
* - the time of first computed solution contained in the first valid MID2 message
* - the time of last computed solution contained in the last valid MID2 message
* - the elevation and SNR masks used by the receiver contained in the first valid MID19 message
*<p>
* The method iterates over the input file extracting messages until above describe data are acquired
* or it reaches the end of file.
*<p>
* It logs at FINE level a message stating which header data have been acquired or not.
*
* @param rtko the RTKobservation object where data got from receiver will be placed
* @return true if all above described header data are properly extracted, false otherwise
*/
bool GNSSdataFromOSP::acqHeaderData(RTKobservation &rtko) {
bool maskSet = false; //mask data set
bool fetSet = false; //first epoch time set
int mid;
//acquire mask data and first and last epoch time
plog->info("RTK header data acquisition:");
while (message.fill(ospFile)) { //there are messages in the binary file
mid = message.get(); //get first byte (MID)
switch(mid) {
case 2:
if (getMID2PosData(rtko)) {
if (!fetSet) {
rtko.setStartTime();
fetSet = true;
}
rtko.setEndTime();
}
break;
case 19: //collect MID19 with masks
maskSet = getMID19Masks(rtko);
break;
default:
break;
}
}
//log data sources available or not
string logMessage = "Header data adquired:";
logMessage += fetSet? "1ts epoch time;" : ";";
logMessage += maskSet? "Mask data" : "";
plog->info(logMessage);
return maskSet && fetSet;
}
/**acqEpochData extracts observation and time data from binary OSP file messages for a RINEX epoch.
*<p>Epoch RINEX data are contained in a sequence of {MID28} [MID2] [MID7] messages.
*<p>Each MID28 contains observables for a given satellite. Successive MID28 messages belong to the same epoch
* if they have the same receiver time. In each epoch the receiver sends a MID28 message for each satellite
* being tracked.
*<p>After sending the epoch MID28 messages, the receiver would send a MID2 message containing the computed
* position solution data.
*<p>The epoch finishes with a MID7 message. It contains clock data for the current epoch, including its GPS time.
* An unexpected end of epoch occurs when a MID28 message shows different receiver time from former received messages.
* This event would be logged at info level. Former message data are discarded because no time solution for this epoch
* can be computed, and the receiver time is unknown.
*<p>The method acquires data reading messages recorded in the binary file according to the above sequence.
* Epoch data are stored in the RINEX data structure for further generation/printing of RINEX observation records.
* Method returns when it is read the last message (MID2 or MID7) of the current epoch.
*<p>Ephemeris data messages (MID15, MID8, MID70) can appear in any place of the input message sequence. Their data
* would be stored for further generation of the RINEX navigation file.
*<p>Other messages in the input binary file are ignored.
*
* @param rinex the RinexObsData object where data got from receiver will be placed
* @param useMID8G when true GPS navigation data will be acquired from MID8 messages, when false these data would be acquired from MID15
* @param useMID8R when true GLONASS navigation data will be acquired from MID8 messages , when false these data would be acquired from MID70
* @return true when observation data from an epoch messages have been acquired, false otherwise (End Of File reached)
*/
bool GNSSdataFromOSP::acqEpochData(RinexData &rinex, bool useMID8G, bool useMID8R) {
int mid, ch, sv;
bool sameEpoch;
double anObservable;
while (message.fill(ospFile)) { //one message has been read from the binary file
mid = message.get(); //get first byte (MID) from message
switch(mid) {
case 7: //the Rx sends MID7 when position for current epoch is computed (after sending MID28 msgs)
if (getMID7TimeData(rinex)) {
plog->fine("Epoch " + to_string((long double) epochGPStow) + " sats=" + to_string((long long) chSatObs.size()));
if(!chSatObs.empty()) {
for (vector<ChannelObs>::iterator it = chSatObs.begin(); it != chSatObs.end(); it++) {
//convert observables from the OSP units to RINEX units when necessary
//and apply corrections due to clock bias, when requested
anObservable = it->psedrng; //unit are m
if (applyBias && (anObservable != 0.0)) anObservable -= epochClkBias * C1CADJ;
rinex.saveObsData(it->system, it->satPrn, "C1C", anObservable, it->limitOl, it->strgIdx, it->timeT);
anObservable = it->carrPh * L1WLINV; //convert from initial unit (m) to cycles
if (applyBias && (anObservable != 0.0)) anObservable -= epochClkBias * L1CADJ;
rinex.saveObsData(it->system, it->satPrn, "L1C", anObservable, it->limitOl, it->strgIdx, it->timeT);
anObservable = it->carrFq * L1WLINV; //convert from initial unit (m/s) to Hz
if (applyBias && (anObservable != 0.0)) anObservable -= epochClkDrift;
rinex.saveObsData(it->system, it->satPrn, "D1C", anObservable, it->limitOl, it->strgIdx, it->timeT);
rinex.saveObsData(it->system, it->satPrn, "S1C", it->signalStrg, it->limitOl, it->strgIdx, it->timeT);
}
chSatObs.clear();
return true;
}
}
break;
case 8: //collect 50BPS ephemerides data in MID8
if (useMID8G || useMID8R) {
try {
//extract channel number an satellite number from the OSP message
ch = (int) message.get();
if (ch>=0 && ch<MAXCHANNELS) { //channel in range, continue data extraction
sv = (int) message.get(); //the satellite number
if ((sv >= FIRSTGPSSAT) && (sv <= LASTGPSSAT)) {
if (useMID8G) getMID8GPSNavData(ch, sv, rinex);
} else if ((sv >= FIRSTGLOSAT) && (sv <= LASTGLOSAT)) { //it is a GLONASS satellite SirfV
if (useMID8R) getMID8GLONavData(ch, sv, rinex);
} else {
plog->warning(msgMID8Ign + " satellite number out of GPS, GLONASS ranges:" + to_string((long long) sv));
}
} else plog->warning(msgMID8Ign + "channel not in range");
} catch (int error) {
plog->severe(msgMID8Ign + msgEOM + to_string((long long) error));
}
}
break;
case 15: //collect complete GPS ephemerides data in MID15
if (!useMID8G) getMID15NavData(rinex);
break;
case 28: //collect satellite measurements from a channel in MID28
if (getMID28ObsData(rinex, sameEpoch)) { //message data are correct and have been stored
if (!sameEpoch) { //last data stored belong to a new epoch, and no MID7 has arrived!
//as no MID7 has been received, the epoch time is not availble and current epoch observables shall be discarded
plog->warning("Epoch " + to_string((long double)chSatObs[0].timeT) + " ignored: MID7 lost");
chSatObs.erase(chSatObs.begin(), chSatObs.end() - 1);
}
}
break;
case 70: //collect complete GLONASS ephemerides data in MID70
if (!useMID8R) getMID70NavData(rinex);
break;
default:
break;
}
}
return false;
}
/**acqGLOparams acquires some relevant parameters from GLONASS navigation data in MID8 messages
*<p>GLONASS navigation data contains:
* - the slot number of the satellite being tracked in a given receiver channel
* - the carrier frequency number for each slot
*<p>Such parameters are needed to stablish correpondence between data provided by receiver
* and used in the GNSS data processing.
*
* @return true if data properly extracted, false otherwise (End Of File reached)
*/
bool GNSSdataFromOSP::acqGLOparams() {
int mid, ch, sat, strNum, n, nA, hnA;
unsigned int gloStrg[3]; //a place to store the 84 bits of the GLONASS nav string
vector<GLONASSslot>::iterator itSlot;
char txtBuffer[80];
bool dataAcq = false;
rewind(ospFile);
plog->info("Acquisition of GLONASS parameters:");
try {
while (message.fill(ospFile)) { //a message has been read from the binary file
mid = message.get(); //get first byte (MID) from message
if (mid == 8) {
CHECK_PAYLOADLEN(43,"MID8 msg len <> 43")
ch = (int) message.get();
if (ch>=0 && ch<MAXCHANNELS) { //channel in range, continue data extraction
sat = (int) message.get(); //the satellite number
if ((sat >= FIRSTGLOSAT) && (sat <= LASTGLOSAT)) { //it is a GLONASS satellite (in SirfV), extract nav data params needed
//get from message payload the GLONASS string and the string number
strNum = getGLOstring(gloStrg);
switch (strNum) {
case 4:
//get slot number (n) in string 4, bits 15-11
//and store its first occurrence in the table of GLONASS satellites
n = getBits(gloStrg, 10, 5);
sat -= FIRSTGLOSAT;
if (satGLOslt[sat].slot == 0) { //if this table entry is empty
satGLOslt[sat].rcvCh = ch;
satGLOslt[sat].slot = n;
}
break;
case 6:
case 8:
case 10:
case 12:
case 14:
//get from almanac data the slot number (nA) in bits 77-73 (see GLONASS ICD for details)
//and prepare slot - carrier frequency table to receive the value corresponding to this slot (if not already received)
nA = getBits(gloStrg, 72, 5);
if (nA > 0 && nA <= MAXGLOSLOTS) {
nAhnA[ch].nA = nA;
nAhnA[ch].strFhnA = strNum + 1; //set the string number where continuation data should come
} else plog->warning("MID8 GLO almanac string " + to_string((long long) strNum) + " bad slot number = " + to_string((long long) nA));
break;
case 7:
case 9:
case 11:
case 13:
case 15:
//check in the slot - carrier frequency table the expected string number in this channel
//if current string is the expected one to provide the carrier frequency data, store it
if (nAhnA[ch].strFhnA == strNum) {
hnA = getBits(gloStrg, 9, 5); //HnA in almanac: bits 14-10
if (hnA >= 25) hnA -= 32; //set negative values as per table 4.11 of the GLONASS ICD
carrierFreq[nAhnA[ch].nA - 1] = hnA;
}
break;
default:
break;
}
}
} else plog->warning(msgMID8Ign + "channel not in range");
}
}
//log data acquired
plog->finer("GLONASS slot numbers used (from string 4 in MID8):");
for (int i=0; i<MAXGLOSATS; i++) {
sprintf(txtBuffer, "->sv=%2d slot=%2d rxChannel=%2d ", i+FIRSTGLOSAT, satGLOslt[i].slot, satGLOslt[i].rcvCh);
plog->finer(string(txtBuffer));
dataAcq = true;
}
plog->finer("GLONASS carrier frequency numbers (from almanac in MID8):");
for (int i = 0; i < MAXGLOSLOTS; i++) {
sprintf(txtBuffer, "->slot=%2d frequency=%2d", i+1, carrierFreq[i]);
plog->finer(string(txtBuffer));
dataAcq = true;
}
} catch (int error) {
plog->severe("MID8 GLO" + msgEOM + to_string((long long) error));
}
return dataAcq;
}
/**acqEpochData acquires epoch position data from binary OSP file messages for RTK observation files.
*<p>Epoch RTK data are contained in a MID2 message.
*<p>The method skips messages from the input binary file until a MID2 message is read.
* When this happens, it stores MID2 data in the RTKobservation object passed and returns.
*
* @param rtko the RTKobservation object where data acquired will be placed
* @return true if epoch position data properly extracted, false otherwise (End Of File reached)
*/
bool GNSSdataFromOSP::acqEpochData(RTKobservation &rtko) {
int mid;
while (message.fill(ospFile)) { //one message has been read from the binary file
mid = message.get(); //get first byte (MID) from message
if ((mid == 2) && getMID2PosData(rtko)) return true;
}
return false;
}
//PRIVATE METHODS
//===============
/**setTblValues set conversion parameter tables used to translate scaled normalized GPS message data to values in actual units.
* Called by the construtors
*
*/
void GNSSdataFromOSP::setTblValues() {
//SV clock data
GPS_SCALEFACTOR[0][0] = pow(2.0, 4.0); //T0c
GPS_SCALEFACTOR[0][1] = pow(2.0, -31.0); //Af0: SV clock bias
GPS_SCALEFACTOR[0][2] = pow(2.0, -43.0); //Af1: SV clock drift
GPS_SCALEFACTOR[0][3] = pow(2.0, -55.0); //Af2: SV clock drift rate
//broadcast orbit 1
GPS_SCALEFACTOR[1][0] = 1.0; //IODE
GPS_SCALEFACTOR[1][1] = pow(2.0, -5.0); //Crs
GPS_SCALEFACTOR[1][2] = pow(2.0, -43.0) * ThisPI; //Delta N
GPS_SCALEFACTOR[1][3] = pow(2.0, -31.0) * ThisPI; //M0
//broadcast orbit 2
GPS_SCALEFACTOR[2][0] = pow(2.0, -29.0); //Cuc
GPS_SCALEFACTOR[2][1] = pow(2.0, -33.0); //e
GPS_SCALEFACTOR[2][2] = pow(2.0, -29.0); //Cus
GPS_SCALEFACTOR[2][3] = pow(2.0, -19.0); //sqrt(A) ????
//broadcast orbit 3
GPS_SCALEFACTOR[3][0] = pow(2.0, 4.0); //TOE
GPS_SCALEFACTOR[3][1] = pow(2.0, -29.0); //Cic
GPS_SCALEFACTOR[3][2] = pow(2.0, -31.0) * ThisPI; //Omega0
GPS_SCALEFACTOR[3][3] = pow(2.0, -29.0); //Cis
//broadcast orbit 4
GPS_SCALEFACTOR[4][0] = pow(2.0, -31.0) * ThisPI; //i0
GPS_SCALEFACTOR[4][1] = pow(2.0, -5.0); //Crc
GPS_SCALEFACTOR[4][2] = pow(2.0, -31.0) * ThisPI; //w
GPS_SCALEFACTOR[4][3] = pow(2.0, -43.0) * ThisPI; //w dot
//broadcast orbit 5
GPS_SCALEFACTOR[5][0] = pow(2.0, -43.0) * ThisPI; //Idot ??
GPS_SCALEFACTOR[5][1] = 1.0; //codes on L2
GPS_SCALEFACTOR[5][2] = 1.0; //GPS week + 1024
GPS_SCALEFACTOR[5][3] = 1.0; //L2 P data flag
//broadcast orbit 6
GPS_SCALEFACTOR[6][0] = 1.0; //SV accuracy (index)
GPS_SCALEFACTOR[6][1] = 1.0; //SV health
GPS_SCALEFACTOR[6][2] = pow(2.0, -31.0); //TGD
GPS_SCALEFACTOR[6][3] = 1.0; //IODC
//broadcast orbit 7
GPS_SCALEFACTOR[7][0] = 0.01; //Transmission time of message in sec x 100
GPS_SCALEFACTOR[7][1] = 1.0; //Fit interval
GPS_SCALEFACTOR[7][2] = 0.0; //Spare
GPS_SCALEFACTOR[7][3] = 0.0; //Spare
//GPS_URA table to obtain in meters the value associated to the satellite accuracy index
//fill GPS_URA table as per GPS ICD 20.3.3.3.1.3
GPS_URA[0] = 2.0; //0
GPS_URA[1] = 2.8; //1
GPS_URA[2] = 4.0; //2
GPS_URA[3] = 5.7; //3
GPS_URA[4] = 8.0; //4
GPS_URA[5] = 11.3; //5
GPS_URA[6] = pow(2.0, 4.0); //<=6
GPS_URA[7] = pow(2.0, 5.0);
GPS_URA[8] = pow(2.0, 6.0);
GPS_URA[9] = pow(2.0, 7.0);
GPS_URA[10] = pow(2.0, 8.0);
GPS_URA[11] = pow(2.0, 9.0);
GPS_URA[12] = pow(2.0, 10.0);
GPS_URA[13] = pow(2.0, 11.0);
GPS_URA[14] = pow(2.0, 12.0);
GPS_URA[15] = 6144.00;
//SV clock data
GLO_SCALEFACTOR[0][0] = 1; //T0c
GLO_SCALEFACTOR[0][1] = pow(2.0, -30.0); //-TauN
GLO_SCALEFACTOR[0][2] = pow(2.0, -40.0); //+GammaN
GLO_SCALEFACTOR[0][3] = 1; //Message frame time
//broadcast orbit 1
GLO_SCALEFACTOR[1][0] = pow(2.0, -11.0); //X
GLO_SCALEFACTOR[1][1] = pow(2.0, -20.0); //Vel X
GLO_SCALEFACTOR[1][2] = pow(2.0, -30.0); //Accel X
GLO_SCALEFACTOR[1][3] = 1; //SV Health
//broadcast orbit 2
GLO_SCALEFACTOR[2][0] = pow(2.0, -11.0); //Y
GLO_SCALEFACTOR[2][1] = pow(2.0, -20.0); //Vel Y
GLO_SCALEFACTOR[2][2] = pow(2.0, -30.0); //Accel Y
GLO_SCALEFACTOR[2][3] = 1; //Frequency number
//broadcast orbit 3
GLO_SCALEFACTOR[3][0] = pow(2.0, -11.0); //Z
GLO_SCALEFACTOR[3][1] = pow(2.0, -20.0); //Vel Z
GLO_SCALEFACTOR[3][2] = pow(2.0, -30.0); //Accel Z
GLO_SCALEFACTOR[3][3] = 1; //Age of oper.
//set tables to 0
memset(subfrmCh, 0, sizeof subfrmCh);
memset(satGLOslt, 0, sizeof satGLOslt);
memset(carrierFreq, 0, sizeof carrierFreq);
memset(nAhnA, 0, sizeof nAhnA);
}
/**getMID2PosData gets position solution data from a MID2 message and store them into "APPROX POSITION XYZ" record of a RinexData object.
*
*@param rinex the object where acquired data are stored
*@return true if data properly extracted (correct message length and satellites in solution greather than minimum), false otherwise
*/
bool GNSSdataFromOSP::getMID2PosData(RinexData &rinex) {
float x, y, z;
int nsv;
bool status;
try {
if (status = getMID2xyz (x, y, z, nsv)) status = rinex.setHdLnData(RinexData::APPXYZ, x, y, z);
} catch (string error) {
plog->severe(error + " in getMID2");
return false;
}
return status;
}
/**getMID2PosData gets position solution data (X, Y, Z coordinates and time) from a MID2 message and store them into a RTKobservation object.
*
*@param rtko the object where acquired data are stored
*@return true if data properly extracted (correct message length and satellites in solution greather than minimum), false otherwise
*/
bool GNSSdataFromOSP::getMID2PosData(RTKobservation &rtko) {
float x, y, z;
int nsv;
bool status;
//it is assumed that "quality" is 5. No data exits in OSP messages to obtain it
if (status = getMID2xyz (x, y, z, nsv)) rtko.setPosition(epochGPSweek, epochGPStow, (double) x, (double) y, (double) z, 5, nsv);
return status;
}
/**getMID2xyz gets position solution data (X, Y, Z coordinates and time) from a MID2 message.
* X, Y, Z data are provided througth parameters, time data are updated in the object (epoch week and TOW)
*
*@param x the x coordinate
*@param y the y coordinate
*@param z the z coordinate
*@param nsv the number of satellites used to compute the solution
*@return true if data properly extracted (correct message length and satellites in solution greather than minimum), false otherwise
*/
bool GNSSdataFromOSP::getMID2xyz(float &x, float &y, float &z, int &nsv) {
char msgBuf[100];
CHECK_PAYLOADLEN(41,"MID2 msg len <> 41")
//extract X, Y, Z for the rinex header
try {
x = (float) message.getInt(); //get X
y = (float) message.getInt(); //get Y
z = (float) message.getInt(); //get Z
message.skipBytes(9); //skip from vX to Mode2: 3*2S 3*1U
epochGPSweek = (int) message.getUShort() + 1024;
epochGPStow = (double) message.getInt() / 100.0; //get GPS TOW (scaled by 100)
//check if fix has the minimum SVs required
nsv = message.get();
CHECK_SATSREQUIRED(nsv, "MID2" + msgFew)
sprintf(msgBuf, "MID2 tow=%g x=%g y=%g z=%g", epochGPStow, x, y, z);
plog->finer(string(msgBuf));
} catch (int error) {
plog->severe("MID2 " + msgEOM + to_string((long long) error));
return false;
}
return true;
}
/**getMID6RxData gets receiver identification data from a MID6 message. Data acquired are stored in the RECEIVER record of the RinexData object.
*
*@param rinex the class instance where data are stored
*@return true if data properly extracted, false otherwise
*/
bool GNSSdataFromOSP::getMID6RxData(RinexData &rinex) {
//Note: current structure of this message does not correspond with what is stated in ICD
string swVersion;
string swCustomer;
int SWversionLen;
int SWcustomerLen;
int msgLen;
char c;
try {
SWversionLen = message.get();
SWcustomerLen = message.get();
//verify length of fields and message
msgLen = 1 + 2 + SWversionLen + SWcustomerLen;
CHECK_PAYLOADLEN(msgLen,"In MID6, message/receiver/customer length do not match")
//extract swVersion from message char by char
while (SWversionLen-- > 0) {
c = (char) (message.get() & 0xFF);
swVersion += c;
}
//extract swCustomer from buffer
while (SWcustomerLen-- > 0) {
c = (char) (message.get() & 0xFF);
swCustomer += c;
}
rinex.setHdLnData(RinexData::RECEIVER, swVersion, receiver, swCustomer);
} catch (int error) {
plog->severe("MID6" + msgEOM + to_string((long long) error));
return false;
} catch (string error) {
plog->severe(error + " in getMID6");
return false;
}
plog->finer("MID6 swV=" + swVersion + " swC=" + swCustomer);
return true;
}
/**getMID7TimeData gets GPS time data from a MID7 message. Data acquired are stored as current epoch time into the RinexData object.
*
* @param rinex the class instance where data will be stored
* @return true if data properly extracted (correct message length and satellites in solution greather than minimum), false otherwise
*/
bool GNSSdataFromOSP::getMID7TimeData(RinexData &rinex) {
int sats;
char msgBuf[100];
CHECK_PAYLOADLEN(20,"MID7 msg len <> 20")
try {
epochGPSweek = (int) message.getUShort(); //get GPS Week (includes rollover)
epochGPStow = (double) message.getUInt(); //get GPS TOW
epochGPStow /= 100.0; //... is scaled by 100
sats = (int) message.get(); //get number of satellites in the solution
CHECK_SATSREQUIRED(sats, "MID7" + msgFew)
epochClkDrift = (double) message.getUInt(); //get receiver clock drift (change rate of bias in Hz)
//get receiver clock bias in nanoseconds (unsigned 32 bits int) and convert to seconds
epochClkBias = (double) message.getUInt() * 1.0e-9;
if (!applyBias) {
epochGPStow += epochClkBias;
epochClkBias = 0.0;
}
} catch (int error) {
plog->severe("MID7TimeData" + msgEOM + to_string((long long) error));
return false;
}
rinex.setEpochTime(epochGPSweek, epochGPStow, epochClkBias, 0);
sprintf(msgBuf, "MID7 time week=%d tow=%g bias=%g", epochGPSweek, epochGPStow, epochClkBias);
plog->finer(string(msgBuf));
return true;
}
/**getMID7Interval get GPS time data from MID7 message, compute interval from current epoch time and store them into INTERVAL record of the RinexData object.
*
* @param rinex the class instance where data will be stored
* @return true if data properly extracted (correct message length and satellites in solution greather than minimum), false otherwise
*/
bool GNSSdataFromOSP::getMID7Interval(RinexData &rinex) {
int week, sats;
double tow, interval;
char msgBuf[100];
CHECK_PAYLOADLEN(20,"MID7 msg len <> 20")
try {
week = (int) message.getUShort(); //get GPS Week (includes rollover)
tow = (double) message.getUInt(); //get GPS TOW
tow /= 100.0; //.. is scaled by 100)
sats = (int) message.get(); //get number of satellites in the solution
CHECK_SATSREQUIRED(sats, "MID7" + msgFew)
interval = tow - epochGPStow + (double) ((week - epochGPSweek) * 604800.0);
rinex.setHdLnData(rinex.INT, interval);
} catch (int error) {
plog->severe("MID7interval" + msgEOM + to_string((long long) error));
return false;
} catch (string error) {
plog->severe(error + " in getMID7interval");
return false;
}
sprintf(msgBuf, "MID7 interval week=%d tow=%g interval=%g", week, tow, interval);
plog->finer(string(msgBuf));
return true;
}
/**getMID8GPSNavData gets GPS navigation data from a MID 8 message and store them into satellite ephemeris (bradcast orbit data) of the RinexData object.
*
* @param ch the receiver channel number providing data
* @param sv the satellite number given by the receiver
* @param rinex the class instance where data are stored
* @return true if data properly extracted (correct message length, channel number in range, and correct parity in navigation data), false otherwise
*/
bool GNSSdataFromOSP::getMID8GPSNavData(int ch, int sv, RinexData &rinex) {
CHECK_PAYLOADLEN(43,"MID8 msg len <> 43")
unsigned int wd[10]; //a place to store the ten words of OSP message
unsigned int navW[45]; //a place to pack message data as per MID 15 (see SiRF ICD)
unsigned int sat; //the satellite number in the satellite navigation message
int bom[8][4]; //the RINEX broadcats orbit like arrangement for satellite ephemeris mantissa
double tTag; //the time tag for ephemeris data
double bo[8][4]; //the RINEX broadcats orbit arrangement for satellite ephemeris
bool parityOK;
unsigned int subfrmID, pgID;
char msgBuf[100];
try {
//read ten words with navigation data from the OSP message. Bits in each 32 bits word are: D29 D30 d1 d2 ... d30
//that is: two last parity bits from previous word followed by the 30 bits of the current word
for (int i=0; i<10; i++) wd[i] = message.getUInt();
//check parity of each subframe word
parityOK = checkGPSparity(wd[0]);
for (int i=1; parityOK && i<10; i++) parityOK &= checkGPSparity(wd[i]);
//if parity not OK, ignore all subframe data and return
if (!parityOK) {
plog->warning(msgMID8Ign + "GPS wrong parity");
return false;
}
//remove parity from each GPS word getting the useful 24 bits
//Note that when D30 is set, data bits are complemented (a non documented SiRF OSP feature)
for (int i=0; i<10; i++)
if ((wd[i] & 0x40000000) == 0) wd[i] = (wd[i]>>6) & 0xFFFFFF;
else wd[i] = ~(wd[i]>>6) & 0xFFFFFF;
//get subframe and page identification (page identification valid only for subframes 4 & 5)
subfrmID = (wd[1]>>2) & 0x07;
pgID = (wd[2]>>16) & 0x3F;
sprintf(msgBuf, "MID8 GPS ch=%d sv=%d subfrm=%d page=%d", ch, sv, subfrmID, pgID);
plog->finer(string(msgBuf));
//only have interest subframes: 1,2,3 & page 18 of subframe 4 (pgID = 56 in GPS ICD Table 20-V)
if ((subfrmID>0 && subfrmID<4) || (subfrmID==4 && pgID==56)) {
subfrmID--; //convert it to its index
//store satellite number and message words
subfrmCh[ch][subfrmID].sv = sv;
for (int i=0; i<10; i++) subfrmCh[ch][subfrmID].words[i] = wd[i];
//check if all ephemerides have been already received
if (allGPSEphemReceived(ch)) {
//if all 3 frames received , pack their data as per MID 15 (see SiRF ICD)
for (int i=0; i<3; i++) { //for each subframe index 0, 1, 2
for (int j=0; j<5; j++) { //for each 2 WORDs group
navW[i*15+j*3] = (subfrmCh[ch][i].words[j*2]>>8) & 0xFFFF;
navW[i*15+j*3+1] = ((subfrmCh[ch][i].words[j*2] & 0xFF)<<8) | ((subfrmCh[ch][i].words[j*2+1]>>16) & 0xFF);
navW[i*15+j*3+2] = subfrmCh[ch][i].words[j*2+1] & 0xFFFF;
}
//the exception is WORD1 (TLM word) of each subframe, whose data are not needed
navW[i*15] = sv;
navW[i*15+1] &= 0xFF;
}
//extract ephemeris data and store them into the RINEX instance
if (extractGPSEphemeris(navW, sat, bom)) {
scaleGPSEphemeris(bom, tTag, bo);
rinex.saveNavData('G', sat, bo, tTag);
}
//TBW check if iono data exist & extract and store iono data in subfrmCh[ch][3]
//clear storage
for (int i=0; i<MAXSUBFR; i++) subfrmCh[ch][i].sv = 0;
}
}
} catch (int error) {
plog->severe("MID8" + msgEOM + to_string((long long) error));
return false;
}
return true;
}
/**getMID8GLONavData gets GLONASS navigation data from a MID 8 message and store them into satellite ephemeris (bradcast orbit data) of the RinexData object.
*
* @param ch the receiver channel number providing data
* @param sv the satellite number given by the receiver
* @param rinex the class instance where data are stored
* @return true if data properly extracted (correct message length, channel number in range, and correct parity in navigation data), false otherwise
*/
bool GNSSdataFromOSP::getMID8GLONavData(int ch, int sv, RinexData &rinex) {
CHECK_PAYLOADLEN(43,"MID8 msg len <> 43")
int sltNum, svx; //the slot number (n) extracted from from string 4
double tTag; //the time tag for ephemeris data
unsigned int gloStrg[3];//a place to store the 84 bits of a GLONASS string
int bom[8][4]; //the RINEX broadcats orbit like arrangement for satellite ephemeris mantissa (as extracted from nav message)
double bo[8][4]; //the RINEX broadcats orbit arrangement for satellite ephemeris (after applying scale factors)
unsigned int strNum; //the GLONASS string number
string msgTxt; //a place to build log messages
unsigned int sat = sv; //the satellite number in the satellite navigation message (slot number for GLONASS). Initially the one given by the receiver
try {
//get from message payload the GLONASS string and the string number
strNum = getGLOstring(gloStrg);
if (!checkGLOhamming (gloStrg)) {
plog->warning(msgMID8Ign + "GLONASS wrong Hamming code");
return false;
}
msgTxt = "MID8 GLONASS ch=" + to_string((long long) ch) + " sv=" + to_string((long long) sv) + " str=" + to_string((long long) strNum);
//store satellite number and message words with inmediate data (strings # 1 to 5)
if ((strNum > 0) && (strNum <= MAXSUBFR)) {
//if string received is 4, it could be necessary to update inmediately the slot number
if (strNum == 4) {
//get slot number (n) in string 4, bits 15-11 and update the table of GLONASS satellites
sltNum = getBits(gloStrg, 10, 5);
if ((sltNum >= 0) && (sltNum <= MAXGLOSATS)) {
svx = sv - FIRSTGLOSAT;
if (satGLOslt[svx].slot != sltNum) {
plog->finer(msgTxt
+ " slot=" + to_string((long long) satGLOslt[svx].slot)
+ " updated to slot=" + to_string((long long) sltNum));
satGLOslt[svx].rcvCh = ch;
satGLOslt[svx].slot = sltNum;
}
} else {
msgTxt += " wrong slot=" + to_string((long long) sltNum);
}
}
strNum--; //convert string number to its index
//store satellite number and message words
subfrmCh[ch][strNum].sv = sv;
for (int i=0; i<3; i++) subfrmCh[ch][strNum].words[i] = gloStrg[i];
for (int i=3; i<10; i++) subfrmCh[ch][strNum].words[i] = 0;
//check if all ephemerides have been already received
msgTxt += " saved";
if (allGLOEphemReceived(ch)) {
//extract ephemeris data and store them into the RINEX instance
if (extractGLOEphemeris(ch, sat, tTag, bom)) {
scaleGLOEphemeris(bom, bo);
rinex.saveNavData('R', sat, bo, tTag);
}
//clear storage
for (int i=0; i<MAXSUBFR; i++) subfrmCh[ch][i].sv = 0;
}
} else msgTxt += " ignored";
plog->finer(msgTxt);
} catch (int error) {
plog->severe("MID8 GLO" + msgEOM + to_string((long long) error));
return false;
}
return true;
}
/**getMID15NavData gets GPS ephemeris data from a MID 15 message
*
* @param rinex the class instance where data are stored
* @return if data properly extracted (correct message length), false otherwise
*/
bool GNSSdataFromOSP::getMID15NavData(RinexData &rinex) {
CHECK_PAYLOADLEN(92,"MID15 msg len <> 92")
unsigned int navW[45]; //to store the 3x15 data items in the message
unsigned int sat; //the satellite number in the satellite navigation message
int bom[8][4]; //the RINEX broadcats orbit like arrangement for satellite ephemeris mantissa
double tTag; //the time tag for ephemeris data
double bo[8][4]; //the RINEX broadcats orbit arrangement for satellite ephemeris
int svID;
string msgMID ("MID15 GPS ephemeris sv=");
try {
svID = (int) message.get();
msgMID += to_string((long long) svID);
for (int i=0; i<45; i++) navW[i] = (unsigned int) message.getUShort();
//set HOW bits in navW[1] and navW[2] to 0 (MID15 does not provide data from HOW)
navW[1] &= 0xFF00;
navW[2] &= 0x0003;
//extract ephemerides data and store them into the RINEX instance
if (!extractGPSEphemeris(navW, sat, bom)) {
plog->warning(msgMID + " Wrong data");
return false;
}
plog->finer(msgMID + " Ephemeris OK");
//set bom[7][0] (MID15 has no HOW data) with current GPS seconds scaled by 100 as transmission time
bom[7][0] = (int) (epochGPStow * 100.0);
scaleGPSEphemeris(bom, tTag, bo);
rinex.saveNavData('G', sat, bo, tTag);
} catch (int error) {
plog->severe("MID15" + msgEOM + to_string((long long) error));
return false;
}
return true;
}
/**getMID19Masks gets receiver setting data (elevation and SNR masks) from a MID 19 message
*
* @param rtko the RTKobservation class instance where data are stored
* @return true if data properly extracted (correct message length), false otherwise
*/
bool GNSSdataFromOSP::getMID19Masks(RTKobservation &rtko) {
CHECK_PAYLOADLEN(65,"MID19 msg len <> 65")
double elevationMask;
double snrMask;
try {
message.skipBytes(19); //skip from SubID to DOPmask: 1U 2U 3*1U 2S 6*1U 4U 1U
elevationMask = (double) message.getShort();
snrMask = (double) message.get();
rtko.setMasks(elevationMask/10.0, snrMask);
} catch (int error) {
plog->severe("MID19" + msgEOM + to_string((long long) error));
return false;
}
plog->finer("MID19 elevation=" + to_string((long double) elevationMask) + " s/n=" + to_string((long double) snrMask));
return true;
}
/**getMID28ObsData gets observables / measurements (time, pseudorange, carrier phase, etc.) for a satellite from a MID28 message
*
* @param rinex the RinexData class instance where data are to be stored
* @param sameEpoch is set to true when measurements belongs to the current epoch and have been stored in the rinex object, set to false otherwise
* @return true if data properly extracted (correct message length and receiver gives confidence on observables), false otherwise
*/
bool GNSSdataFromOSP::getMID28ObsData(RinexData &rinex, bool &sameEpoch) {
char sys;
int channel, sv, satID, syncFlags, carrier2noise, strength, strengthIndex;
unsigned short int deltaRangeInterval;
double gpsSWtime, pseudorange, carrierFrequency, carrierPhase;
char msgBuf[100];
CHECK_PAYLOADLEN(56,"MID28 msg len <> 20")
sameEpoch = false;
try {
//get data from message MID28
channel = message.get();
message.getInt(); //a time tag not used
sv = message.get(); //the satellite number assigned by the receiver
if ((sv >= FIRSTGPSSAT) && (sv <= LASTGPSSAT)) {
sys = 'G';
satID = sv;
} else if ((sv >= FIRSTGLOSAT) && (sv <= LASTGLOSAT)) { //it is a GLONASS satellite SirfV
sys = 'R';
satID = getGLOslot(channel, sv);
} else if ((sv >= FIRSTSBASSAT) && (sv <= LASTSBASSAT)) { //it is a SBAS satellite
sys = 'S';
satID = sv - 100;
} else {
plog->warning("MID28 satellite number out of GPS, SBAS, GLONASS ranges:" + to_string((long long) sv));
return false;
}
gpsSWtime = message.getDouble();
pseudorange = message.getDouble();
carrierFrequency = (double) message.getFloat(); //sign - �?
carrierPhase = message.getDouble();
message.getUShort(); //the timeIntrack is not used
syncFlags = message.get();
//get the signal strength as the worst of the C/N0 given
carrier2noise = 0;
strength = message.get();
for (int i=1; i<10; i++)
if ((carrier2noise = message.get()) < strength) strength = carrier2noise;
deltaRangeInterval = message.getUShort();
} catch (int error) {
plog->severe("MID28 " + msgEOM + to_string((long long) error));
return false;
}
sprintf(msgBuf,"MID28 tTag=%g ch=%2d sv=%2d sat=%c%02d psr=%g SynFlg=%02X ", gpsSWtime, channel, sv, sys, satID, pseudorange, syncFlags);
//compute strengthIndex as per RINEX spec (5.7): min(max(strength / 6, 1), 9)
strengthIndex = strength / 6;
if (strengthIndex < 1) strengthIndex = 1;
if (strengthIndex > 9) strengthIndex = 9;
//TBW Phase Error Count could be used to compute LoL: if for this satellite PhEC != former PhEC, there is a slip in the carrierPhase
if ((syncFlags & 0x01) != 0) { //bit 0 is set only when acquisition is complete
if ((syncFlags & 0x02) == 0) carrierPhase = 0.0;
if ((syncFlags & 0x10) == 0) carrierFrequency = 0.0;
chSatObs.push_back(ChannelObs(sys, satID, pseudorange, carrierPhase, carrierFrequency, (double) strength, 0, strengthIndex, gpsSWtime));
sameEpoch = gpsSWtime == chSatObs[0].timeT;
plog->finer(string(msgBuf) + "SAVED");
return true;
}
plog->finer(string(msgBuf) + "IGNORED");
return false;
}
/**getMID70NavData gets GLONASS ephemeris data from a MID 70 SID 12 message
*
* @param rinex the class instance where data are stored
* @return if data properly extracted (correct SID data), false otherwise
*/
bool GNSSdataFromOSP::getMID70NavData(RinexData &rinex) {
//----------
//incomplete and not verified code
//----------
unsigned int sat; //the satellite number in the satellite navigation message
int bom[8][4]; //the RINEX broadcats orbit like arrangement for satellite ephemeris mantissa
double tTag; //the time tag for ephemeris data
double bo[8][4]; //the RINEX broadcats orbit arrangement for satellite ephemeris
int tauGPS, tauUTC, b1, b2, n4, kp, nSvs, day, time;
bool validEphem;
//CHECK_PAYLOADLEN(,"MID70 msg len <> ")
try {
if (message.get() != 12) return false; //SID is not for a GLONASS Broadcast Ephemeris Response message
if (message.get() != 1) return false; //Fields TAU_GPS through KP are not valid, and some data becomes ambiguous
tauGPS = message.getInt3();
tauUTC = message.getInt();
b1 = message.getShort();
b2 = message.getShort();
n4 = message.get();
kp = message.get();
nSvs = message.get();
plog->finer("MID70 SID12 GLONASS ephem. for nSVs=" + to_string((long long) nSvs));
while (nSvs > 0) {
validEphem = message.get() == 1; //Validity flag
sat = message.get(); //Slot number
if (validEphem && (sat > 0) && (sat <= MAXGLOSATS)) {
bom[2][3] = message.get(); //Frequency offset
bom[1][3] = message.get(); //Satellite health
day = message.getUShort(); //day number
time = message.get() * 900; //Ephemeris reference time
tTag = getInstantGPSdate(1996 + n4*4, 0, day, 0, 0, (float) time); //convert ephmeris time (in GLONASS time) to an instant (use GPS ephemeris for convenience)
tTag -= 3*60*60; //correct GLONASS time to UTC
bom[0][0] = (int) tTag; //Toc
bom[3][3] = message.get(); //Age of operational information
bom[1][0] = message.getInt(); //Satellite position, X
bom[2][0] = message.getInt(); //Satellite position, Y
bom[3][0] = message.getInt(); //Satellite position, Z
bom[1][1] = message.getInt3(); //Satellite velocity, X
bom[2][1] = message.getInt3(); //Satellite velocity, Y
bom[3][1] = message.getInt3(); //Satellite velocity, Z
bom[1][2] = message.get(); //Satellite acceleration, X
bom[2][2] = message.get(); //Satellite acceleration, Y
bom[3][2] = message.get(); //Satellite acceleration, Z
message.get(); //Group delay
bom[0][1] = -message.getInt3(); //SV clock bias (sec) (-TauN) <- Correction to satellite clock with respect to GLONASS system time(-TauN?)
bom[0][2] = carrierFreq[sat-1]; //SV relative frequency bias +GammaN
bom[0][3] = (int) tTag; //Message frame time (tk+nd*86400) in seconds of the UTC week?
bom[3][3] = 0; //Age of oper. information (days) (E)
scaleGLOEphemeris(bom, bo);
rinex.saveNavData('R', sat, bo, tTag);
} else plog->warning("GLONASS ephem. not valid for " + to_string((long long) sat));
nSvs--;
}
} catch (int error) {
plog->severe("MID70 SID12" + msgEOM + to_string((long long) error));
return false;
}
return true;
}
/**checkGPSparity checks the parity of a GPS message subframe word using procedure in GPS ICD.
* To check the parity, the six bits of parity are computed for the word contents, and then compared with the current parity in the 6 LSB of the word passed.
*
* @param d The subframe word passed, where the two LSB bits of the previous word have been added
* @return true if parity computed is equal to the current parity in the six LSB of the word
*/
bool GNSSdataFromOSP::checkGPSparity (unsigned int d) {
//d has de form: D29 D30 d1 .. d30)
unsigned int toCheck = d;
if ((d & 0x40000000) != 0) toCheck = (d & 0xC0000000) | (~d & 0x3FFFFFFF);
//compute the parity of the bit stream
unsigned int parity = 0;
for (int i=0; i<6; i++) parity |= (bitsSet(parityBitMask[i] & toCheck) % 2) << (5-i);
return parity == (d & 0x3F);
}
/**checkGLOhamming checks the GLONASS string for correct hamming code.
*
* @param ps a pointer to the string to be checked contained in the 3 words pointed by pw
* @return true if code computed is equal to the current one in bits 1-8 of the string
*/
bool GNSSdataFromOSP::checkGLOhamming (unsigned int* ps) {
//To be implemented
return true;
};