source: ntrip/trunk/BNC/src/ephemeris.cpp@ 10954

Last change on this file since 10954 was 10954, checked in by stuerze, 36 hours ago

minor changes to investigate a special GLO behavior

File size: 90.0 KB
Line 
1#include <sstream>
2#include <iostream>
3#include <iomanip>
4#include <cstring>
5
6#include <newmatio.h>
7
8#include "ephemeris.h"
9#include "bncutils.h"
10#include "bnctime.h"
11#include "bnccore.h"
12#include "bncutils.h"
13#include "satObs.h"
14#include "pppInclude.h"
15#include "pppModel.h"
16#include "RTCM3/bits.h"
17
18using namespace std;
19
20// Constructor
21////////////////////////////////////////////////////////////////////////////
22t_eph::t_eph() {
23 _checkState = unchecked;
24 _type = undefined;
25 _orbCorr = 0;
26 _clkCorr = 0;
27}
28// Destructor
29////////////////////////////////////////////////////////////////////////////
30t_eph::~t_eph() {
31 if (_orbCorr)
32 delete _orbCorr;
33 if (_clkCorr)
34 delete _clkCorr;
35}
36
37//
38////////////////////////////////////////////////////////////////////////////
39void t_eph::setOrbCorr(const t_orbCorr *orbCorr) {
40 if (_orbCorr) {
41 delete _orbCorr;
42 _orbCorr = 0;
43 }
44 _orbCorr = new t_orbCorr(*orbCorr);
45}
46
47//
48////////////////////////////////////////////////////////////////////////////
49void t_eph::setClkCorr(const t_clkCorr *clkCorr) {
50 if (_clkCorr) {
51 delete _clkCorr;
52 _clkCorr = 0;
53 }
54 _clkCorr = new t_clkCorr(*clkCorr);
55}
56
57//
58////////////////////////////////////////////////////////////////////////////
59t_irc t_eph::getCrd(const bncTime &tt, ColumnVector &xc, ColumnVector &vv,
60 bool useCorr) const {
61
62 if (_checkState == bad ||
63 _checkState == unhealthy ||
64 _checkState == outdated) {
65 return failure;
66 }
67
68 xc.ReSize(6);
69 vv.ReSize(3);
70 if (position(tt.gpsw(), tt.gpssec(), xc.data(), vv.data()) != success) {
71 return failure;
72 }
73 if (useCorr) {
74 if (_orbCorr && _clkCorr) {
75 // Refuse to extrapolate a correction far beyond its own declared
76 // update interval - without this, a single missed/dropped
77 // correction message (network blip, encoder hiccup, ...) gets
78 // extrapolated via its rate term (_dotXr/_dotDClk) forever, with no
79 // warning, producing an unbounded, silently growing position error.
80 // ----------------------------------------------------------------
81 // _updateInt defaults to 0 (1s) when not explicitly set by the
82 // source stream, so a pure multiple of it would be far too strict -
83 // floor it at MIN_STALE_BOUND so normal, slightly slower-than-1Hz
84 // streams are not falsely flagged.
85 const double MAX_STALE_FACTOR = 3.0;
86 const double MIN_STALE_BOUND = 60.0; // [s]
87 double maxAgeO = max(MIN_STALE_BOUND, MAX_STALE_FACTOR * ssrUpdateInt[_orbCorr->_updateInt]);
88 double maxAgeC = max(MIN_STALE_BOUND, MAX_STALE_FACTOR * ssrUpdateInt[_clkCorr->_updateInt]);
89 if (fabs(tt - _orbCorr->_time) > maxAgeO ||
90 fabs(tt - _clkCorr->_time) > maxAgeC) {
91 return failure;
92 }
93
94 double dtO = tt - _orbCorr->_time;
95 if (_orbCorr->_updateInt) {
96 dtO -= (0.5 * ssrUpdateInt[_orbCorr->_updateInt]);
97 }
98 ColumnVector dx(3);
99 dx[0] = _orbCorr->_xr[0] + _orbCorr->_dotXr[0] * dtO;
100 dx[1] = _orbCorr->_xr[1] + _orbCorr->_dotXr[1] * dtO;
101 dx[2] = _orbCorr->_xr[2] + _orbCorr->_dotXr[2] * dtO;
102
103 RSW_to_XYZ(xc.Rows(1, 3), vv.Rows(1, 3), dx, dx);
104
105 xc[0] -= dx[0];
106 xc[1] -= dx[1];
107 xc[2] -= dx[2];
108
109 ColumnVector dv(3);
110 RSW_to_XYZ(xc.Rows(1, 3), vv.Rows(1, 3), _orbCorr->_dotXr, dv);
111
112 vv[0] -= dv[0];
113 vv[1] -= dv[1];
114 vv[2] -= dv[2];
115
116 double dtC = tt - _clkCorr->_time;
117 if (_clkCorr->_updateInt) {
118 dtC -= (0.5 * ssrUpdateInt[_clkCorr->_updateInt]);
119 }
120 xc[3] += _clkCorr->_dClk + _clkCorr->_dotDClk * dtC
121 + _clkCorr->_dotDotDClk * dtC * dtC;
122 } else {
123 return failure;
124 }
125 }
126 return success;
127}
128
129//
130//////////////////////////////////////////////////////////////////////////////
131void t_eph::setType(QString typeStr) {
132
133 if (typeStr == "LNAV") {
134 _type = t_eph::LNAV;
135 } else if (typeStr == "FDMA") {
136 _type = t_eph::FDMA;
137 } else if (typeStr == "FNAV") {
138 _type = t_eph::FNAV;
139 } else if (typeStr == "INAV") {
140 _type = t_eph::INAV;
141 } else if (typeStr == "D1") {
142 _type = t_eph::D1;
143 } else if (typeStr == "D2") {
144 _type = t_eph::D2;
145 } else if (typeStr == "SBAS") {
146 _type = t_eph::SBASL1;
147 } else if (typeStr == "CNAV") {
148 _type = t_eph::CNAV;
149 } else if (typeStr == "CNV1") {
150 _type = t_eph::CNV1;
151 } else if (typeStr == "CNV2") {
152 _type = t_eph::CNV2;
153 } else if (typeStr == "CNV3") {
154 _type = t_eph::CNV3;
155 } else if (typeStr == "L1NV") {
156 _type = t_eph::L1NV;
157 } else if (typeStr == "L1OC") {
158 _type = t_eph::L1OC;
159 } else if (typeStr == "L3OC") {
160 _type = t_eph::L3OC;
161 } else {
162 _type = t_eph::undefined;
163 }
164
165}
166
167
168//
169//////////////////////////////////////////////////////////////////////////////
170QString t_eph::typeStr(e_type type, const t_prn &prn, double version) {
171 QString typeStr = "";
172 QString epochStart;
173 QString eolStr;
174
175 if (version < 4.0) {
176 return typeStr;
177 }
178
179 if (version == 99.0) { // log output for OUTDATED, WRONG or UNHEALTHY satellites
180 epochStart = "";
181 eolStr = "";
182 } else {
183 epochStart = "> ";
184 eolStr = "\n";
185 }
186
187 QString ephStr = QString("EPH %1 ").arg(prn.toString().c_str());
188 switch (type) {
189 case undefined:
190 typeStr = epochStart + ephStr + "unknown" + eolStr;
191 break;
192 case LNAV:
193 typeStr = epochStart + ephStr + "LNAV" + eolStr;
194 break;
195 case FDMA:
196 case FDMA_M:
197 typeStr = epochStart + ephStr + "FDMA" + eolStr;
198 break;
199 case FNAV:
200 typeStr = epochStart + ephStr + "FNAV" + eolStr;
201 break;
202 case INAV:
203 typeStr = epochStart + ephStr + "INAV" + eolStr;
204 break;
205 case D1:
206 typeStr = epochStart + ephStr + "D1 " + eolStr;
207 break;
208 case D2:
209 typeStr = epochStart + ephStr + "D2 " + eolStr;
210 break;
211 case SBASL1:
212 typeStr = epochStart + ephStr + "SBAS" + eolStr;
213 break;
214 case CNAV:
215 typeStr = epochStart + ephStr + "CNAV" + eolStr;
216 break;
217 case CNV1:
218 typeStr = epochStart + ephStr + "CNV1" + eolStr;
219 break;
220 case CNV2:
221 typeStr = epochStart + ephStr + "CNV2" + eolStr;
222 break;
223 case CNV3:
224 typeStr = epochStart + ephStr + "CNV3" + eolStr;
225 break;
226 case L1NV:
227 typeStr = epochStart + ephStr + "L1NV" + eolStr;
228 break;
229 case L1OC:
230 typeStr = epochStart + ephStr + "L1OC" + eolStr;
231 break;
232 case L3OC:
233 typeStr = epochStart + ephStr + "L3OC" + eolStr;
234 break;
235 }
236 return typeStr;
237}
238
239//
240//////////////////////////////////////////////////////////////////////////////
241QString t_eph::rinexDateStr(const bncTime &tt, const t_prn &prn,
242 double version) {
243 QString prnStr(prn.toString().c_str());
244 return rinexDateStr(tt, prnStr, version);
245}
246
247//
248//////////////////////////////////////////////////////////////////////////////
249QString t_eph::rinexDateStr(const bncTime &tt, const QString &prnStr,
250 double version) {
251
252 QString datStr;
253
254 unsigned year, month, day, hour, min;
255 double sec;
256 tt.civil_date(year, month, day);
257 tt.civil_time(hour, min, sec);
258
259 QTextStream out(&datStr);
260
261 if (version < 3.0) {
262 QString prnHlp = prnStr.mid(1, 2);
263 if (prnHlp[0] == '0')
264 prnHlp[0] = ' ';
265 out << prnHlp
266 << QString(" %1 %2 %3 %4 %5%6").arg(year % 100, 2, 10, QChar('0')).arg(
267 month, 2).arg(day, 2).arg(hour, 2).arg(min, 2).arg(sec, 5, 'f', 1);
268 }
269 else if (version == 99) {
270 out
271 << QString(" %1 %2 %3 %4 %5 %6").arg(year, 4).arg(month, 2, 10,
272 QChar('0')).arg(day, 2, 10, QChar('0')).arg(hour, 2, 10, QChar('0')).arg(
273 min, 2, 10, QChar('0')).arg(int(sec), 2, 10, QChar('0'));
274 }
275 else {
276 out << prnStr
277 << QString(" %1 %2 %3 %4 %5 %6").arg(year, 4).arg(month, 2, 10,
278 QChar('0')).arg(day, 2, 10, QChar('0')).arg(hour, 2, 10, QChar('0')).arg(
279 min, 2, 10, QChar('0')).arg(int(sec), 2, 10, QChar('0'));
280 }
281
282 return datStr;
283}
284
285// Constructor
286//////////////////////////////////////////////////////////////////////////////
287t_ephGPS::t_ephGPS(double rnxVersion, const QStringList &lines, QString typeStr) {
288
289 setType(typeStr);
290
291 int nLines = 8; // LNAV
292 // Source RINEX version < 4
293 if (type() == t_eph::undefined) {
294 _type = t_eph::LNAV;
295 }
296
297 if (type() == t_eph::CNAV ||
298 type() == t_eph::L1NV) {
299 nLines += 1;
300 }
301 if (type() == t_eph::CNV2) {
302 nLines += 2;
303 }
304
305 if (lines.size() != nLines) {
306 _checkState = bad;
307 return;
308 }
309
310 // RINEX Format
311 // ------------
312 int fieldLen = 19;
313 double statusflags = 0.0;
314
315 int pos[4];
316 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
317 pos[1] = pos[0] + fieldLen;
318 pos[2] = pos[1] + fieldLen;
319 pos[3] = pos[2] + fieldLen;
320
321 // Read nLines lines
322 // ------------------
323 for (int iLine = 0; iLine < nLines; iLine++) {
324 QString line = lines[iLine];
325
326 if (iLine == 0) {
327 QTextStream in(line.left(pos[1]).toLatin1());
328 int year, month, day, hour, min;
329 double sec;
330
331 QString prnStr, n;
332 in >> prnStr;
333
334 if (prnStr.size() == 1 &&
335 (prnStr[0] == 'G' ||
336 prnStr[0] == 'J' ||
337 prnStr[0] == 'I')) {
338 in >> n;
339 prnStr.append(n);
340 }
341
342 if ( prnStr.at(0) == 'G') {
343 _prn.set('G', prnStr.mid(1).toInt());
344 } else if (prnStr.at(0) == 'J') {
345 _prn.set('J', prnStr.mid(1).toInt());
346 } else if (prnStr.at(0) == 'I') {
347 _prn.set('I', prnStr.mid(1).toInt());
348 } else {
349 _prn.set('G', prnStr.toInt());
350 }
351 _prn.setFlag(type());
352
353 in >> year >> month >> day >> hour >> min >> sec;
354
355 if (year < 80) {
356 year += 2000;
357 } else if (year < 100) {
358 year += 1900;
359 }
360
361 _TOC.set(year, month, day, hour, min, sec);
362
363 if ( readDbl(line, pos[1], fieldLen, _clock_bias)
364 || readDbl(line, pos[2], fieldLen, _clock_drift)
365 || readDbl(line, pos[3], fieldLen, _clock_driftrate)) {
366 _checkState = bad;
367 return;
368 }
369 }
370 // =====================
371 // BROADCAST ORBIT - 1
372 // =====================
373 else if (iLine == 1) {
374 if (type() == t_eph::CNAV ||
375 type() == t_eph::CNV2 ||
376 type() == t_eph::L1NV) {
377 if ( readDbl(line, pos[0], fieldLen, _ADOT)
378 || readDbl(line, pos[1], fieldLen, _Crs)
379 || readDbl(line, pos[2], fieldLen, _Delta_n)
380 || readDbl(line, pos[3], fieldLen, _M0)) {
381 _checkState = bad;
382 return;
383 }
384 } else { // LNAV
385 if ( readDbl(line, pos[0], fieldLen, _IODE)
386 || readDbl(line, pos[1], fieldLen, _Crs)
387 || readDbl(line, pos[2], fieldLen, _Delta_n)
388 || readDbl(line, pos[3], fieldLen, _M0)) {
389 _checkState = bad;
390 return;
391 }
392 }
393 }
394 // =====================
395 // BROADCAST ORBIT - 2
396 // =====================
397 else if (iLine == 2) {
398 if ( readDbl(line, pos[0], fieldLen, _Cuc)
399 || readDbl(line, pos[1], fieldLen, _e)
400 || readDbl(line, pos[2], fieldLen, _Cus)
401 || readDbl(line, pos[3], fieldLen, _sqrt_A)) {
402 _checkState = bad;
403 return;
404 }
405 }
406 // =====================
407 // BROADCAST ORBIT - 3
408 // =====================
409 else if (iLine == 3) {
410 if (type() == t_eph::CNAV ||
411 type() == t_eph::CNV2) {
412 if ( readDbl(line, pos[0], fieldLen, _top)
413 || readDbl(line, pos[1], fieldLen, _Cic)
414 || readDbl(line, pos[2], fieldLen, _OMEGA0)
415 || readDbl(line, pos[3], fieldLen, _Cis)) {
416 _checkState = bad;
417 return;
418 }
419 } else if (type() == t_eph::L1NV) {
420 if ( readDbl(line, pos[0], fieldLen, _IODE)
421 || readDbl(line, pos[1], fieldLen, _Cic)
422 || readDbl(line, pos[2], fieldLen, _OMEGA0)
423 || readDbl(line, pos[3], fieldLen, _Cis)) {
424 _checkState = bad;
425 return;
426 }
427 } else { // LNAV
428 if ( readDbl(line, pos[0], fieldLen, _TOEsec)
429 || readDbl(line, pos[1], fieldLen, _Cic)
430 || readDbl(line, pos[2], fieldLen, _OMEGA0)
431 || readDbl(line, pos[3], fieldLen, _Cis)) {
432 _checkState = bad;
433 return;
434 }
435 }
436 }
437 // =====================
438 // BROADCAST ORBIT - 4
439 // =====================
440 else if (iLine == 4) {
441 if ( readDbl(line, pos[0], fieldLen, _i0)
442 || readDbl(line, pos[1], fieldLen, _Crc)
443 || readDbl(line, pos[2], fieldLen, _omega)
444 || readDbl(line, pos[3], fieldLen, _OMEGADOT)) {
445 _checkState = bad;
446 return;
447 }
448 }
449 // =====================
450 // BROADCAST ORBIT - 5
451 // =====================
452 else if (iLine == 5 && system() != t_eph::NavIC) {
453 if (type() == t_eph::CNAV ||
454 type() == t_eph::CNV2) {
455 if ( readDbl(line, pos[0], fieldLen, _IDOT)
456 || readDbl(line, pos[1], fieldLen, _Delta_n_dot)
457 || readDbl(line, pos[2], fieldLen, _URAI_NED0)
458 || readDbl(line, pos[3], fieldLen, _URAI_NED1)) {
459 _checkState = bad;
460 return;
461 }
462 }
463 else { // LNAV
464 if ( readDbl(line, pos[0], fieldLen, _IDOT)
465 || readDbl(line, pos[1], fieldLen, _L2Codes)
466 || readDbl(line, pos[2], fieldLen, _TOEweek)
467 || readDbl(line, pos[3], fieldLen, _L2PFlag)) {
468 _checkState = bad;
469 return;
470 }
471 }
472 } else if (iLine == 5 && system() == t_eph::NavIC) {
473 if (type() == t_eph::LNAV) {
474 if ( readDbl(line, pos[0], fieldLen, _IDOT)
475 || readDbl(line, pos[2], fieldLen, _TOEweek)) {
476 _checkState = bad;
477 return;
478 }
479 }
480 else if (type() == t_eph::L1NV) {
481 if ( readDbl(line, pos[0], fieldLen, _IDOT)
482 || readDbl(line, pos[1], fieldLen, _Delta_n_dot)
483 || readDbl(line, pos[3], fieldLen, _RSF)) {
484 _checkState = bad;
485 return;
486 }
487 }
488 }
489 // =====================
490 // BROADCAST ORBIT - 6
491 // =====================
492 else if (iLine == 6 && system() != t_eph::NavIC) {
493 if (type() == t_eph::CNAV ||
494 type() == t_eph::CNV2) {
495 if ( readDbl(line, pos[0], fieldLen, _URAI_ED)
496 || readDbl(line, pos[1], fieldLen, _health)
497 || readDbl(line, pos[2], fieldLen, _TGD)
498 || readDbl(line, pos[3], fieldLen, _URAI_NED2)) {
499 _checkState = bad;
500 return;
501 }
502 } else { // LNAV
503 if ( readDbl(line, pos[0], fieldLen, _ura)
504 || readDbl(line, pos[1], fieldLen, _health)
505 || readDbl(line, pos[2], fieldLen, _TGD)
506 || readDbl(line, pos[3], fieldLen, _IODC)) {
507 _checkState = bad;
508 return;
509 }
510 }
511 }
512 else if (iLine == 6 && system() == t_eph::NavIC) {
513 if (type() == t_eph::LNAV) {
514 if ( readDbl(line, pos[0], fieldLen, _ura)
515 || readDbl(line, pos[1], fieldLen, _health)
516 || readDbl(line, pos[2], fieldLen, _TGD)) {
517 _checkState = bad;
518 return;
519 }
520 }
521 else if (type() == t_eph::L1NV) {
522 int i = 0;
523 (!_RSF) ? i = 2 : i = 3;
524 if ( readDbl(line, pos[0], fieldLen, _URAI)
525 || readDbl(line, pos[1], fieldLen, _health)
526 || readDbl(line, pos[i], fieldLen, _TGD)) {
527 _checkState = bad;
528 return;
529 }
530 _ura = accuracyFromIndex(int(_URAI), system());
531 }
532 }
533 // =====================
534 // BROADCAST ORBIT - 7
535 // =====================
536 else if (iLine == 7) {
537 if (type() == t_eph::LNAV) {
538 if (readDbl(line, pos[0], fieldLen, _TOT)) {
539 _checkState = bad;
540 return;
541 }
542 if (system() != t_eph::NavIC) {
543 double fitIntervalRnx;
544 if (readDbl(line, pos[1], fieldLen, fitIntervalRnx)) {
545 //fit interval BLK, do nothing
546 _flags_unknown = true;
547 } else {
548 _flags_unknown = false;
549 if (system() == t_eph::GPS) { // in RINEX specified always as time period for GPS
550 _fitInterval = fitIntervalRnx;
551 }
552 else if (system() == t_eph::QZSS) { // specified as flag for QZSS
553 if (rnxVersion == 3.02) {
554 _fitInterval = fitIntervalRnx; // specified as time period
555 } else {
556 _fitInterval = fitIntervalFromFlag(fitIntervalRnx, _IODC, t_eph::QZSS);
557 }
558 }
559 }
560 }
561 }
562 else if (type() == t_eph::CNAV ||
563 type() == t_eph::CNV2) {
564 if ( readDbl(line, pos[0], fieldLen, _ISC_L1CA)
565 || readDbl(line, pos[1], fieldLen, _ISC_L2C)
566 || readDbl(line, pos[2], fieldLen, _ISC_L5I5)
567 || readDbl(line, pos[3], fieldLen, _ISC_L5Q5)) {
568 _checkState = bad;
569 return;
570 }
571 }
572 else if (type() == t_eph::L1NV) {
573 if (!_RSF) {
574 if ( readDbl(line, pos[0], fieldLen, _ISC_S)
575 || readDbl(line, pos[1], fieldLen, _ISC_L1D)) {
576 _checkState = bad;
577 return;
578 }
579 } else {
580 if ( readDbl(line, pos[2], fieldLen, _ISC_L1P)
581 || readDbl(line, pos[3], fieldLen, _ISC_L1D)) {
582 _checkState = bad;
583 return;
584 }
585 }
586 }
587 }
588 // =====================
589 // BROADCAST ORBIT - 8
590 // =====================
591 else if (iLine == 8) {
592 if (type() == t_eph::CNAV) {
593 if ( readDbl(line, pos[0], fieldLen, _TOT)
594 || readDbl(line, pos[1], fieldLen, _wnop)) {
595 _checkState = bad;
596 return;
597 }
598 if (readDbl(line, pos[2], fieldLen, statusflags)) {
599 _flags_unknown = true;
600 }
601 else {
602 _flags_unknown = false;
603 // Bit 0:
604 _intSF = double(bitExtracted(unsigned(statusflags), 1, 0));
605 // Bit 1:
606 _L2Cphasing = double(bitExtracted(unsigned(statusflags), 1, 1));
607 // Bit 2:
608 _alert = double(bitExtracted(unsigned(statusflags), 1, 2));
609 }
610 }
611 else if (type() == t_eph::CNV2) {
612 if ( readDbl(line, pos[0], fieldLen, _ISC_L1Cd)
613 || readDbl(line, pos[1], fieldLen, _ISC_L1Cp)) {
614 _checkState = bad;
615 return;
616 }
617 }
618 else if (type() == t_eph::L1NV) {
619 if ( readDbl(line, pos[0], fieldLen, _TOT)) {
620 _checkState = bad;
621 return;
622 }
623 }
624 }
625 // =====================
626 // BROADCAST ORBIT - 9
627 // =====================
628 else if (iLine == 9) {
629 if (type() == t_eph::CNV2) {
630 if ( readDbl(line, pos[0], fieldLen, _TOT)
631 || readDbl(line, pos[1], fieldLen, _wnop)) {
632 _checkState = bad;
633 return;
634 }
635 if (readDbl(line, pos[2], fieldLen, statusflags)) {
636 _flags_unknown = true;
637 }
638 else {
639 _flags_unknown = false;
640 // Bit 0:
641 _intSF = double(bitExtracted(unsigned(statusflags), 1, 0));
642 if (system() == t_eph::QZSS) {
643 // Bit 1:
644 _ephSF = double(bitExtracted(unsigned(statusflags), 1, 1));
645 }
646 }
647 }
648 }
649 }
650}
651
652// Compute GPS Satellite Position (virtual)
653////////////////////////////////////////////////////////////////////////////
654t_irc t_ephGPS::position(int GPSweek, double GPSweeks, double *xc,
655 double *vv) const {
656
657 static const double omegaEarth = 7292115.1467e-11;
658 static const double gmGRS = 398.6005e12;
659
660 memset(xc, 0, 6 * sizeof(double));
661 memset(vv, 0, 3 * sizeof(double));
662
663 double a0 = _sqrt_A * _sqrt_A;
664 if (a0 == 0) {
665 return failure;
666 }
667
668 double n0 = sqrt(gmGRS / (a0 * a0 * a0));
669
670 bncTime tt(GPSweek, GPSweeks);
671 double tk = tt - bncTime(int(_TOEweek), _TOEsec);
672
673 double n = n0 + _Delta_n;
674 double M = _M0 + n * tk;
675 double E = M;
676 double E_last;
677 int nLoop = 0;
678 do {
679 E_last = E;
680 E = M + _e * sin(E);
681
682 if (++nLoop == 100) {
683 return failure;
684 }
685 } while (fabs(E - E_last) * a0 > 0.001);
686 double v = 2.0 * atan(sqrt((1.0 + _e) / (1.0 - _e)) * tan(E / 2));
687 double u0 = v + _omega;
688 double sin2u0 = sin(2 * u0);
689 double cos2u0 = cos(2 * u0);
690 double r = a0 * (1 - _e * cos(E)) + _Crc * cos2u0 + _Crs * sin2u0;
691 double i = _i0 + _IDOT * tk + _Cic * cos2u0 + _Cis * sin2u0;
692 double u = u0 + _Cuc * cos2u0 + _Cus * sin2u0;
693 double xp = r * cos(u);
694 double yp = r * sin(u);
695 double OM = _OMEGA0 + (_OMEGADOT - omegaEarth) * tk - omegaEarth * _TOEsec;
696
697 double sinom = sin(OM);
698 double cosom = cos(OM);
699 double sini = sin(i);
700 double cosi = cos(i);
701 xc[0] = xp * cosom - yp * cosi * sinom;
702 xc[1] = xp * sinom + yp * cosi * cosom;
703 xc[2] = yp * sini;
704
705 double tc = tt - _TOC;
706 xc[3] = _clock_bias + _clock_drift * tc + _clock_driftrate * tc * tc;
707
708 // Velocity
709 // --------
710 double tanv2 = tan(v / 2);
711 double dEdM = 1 / (1 - _e * cos(E));
712 double dotv = sqrt((1.0 + _e) / (1.0 - _e)) / cos(E / 2) / cos(E / 2)
713 / (1 + tanv2 * tanv2) * dEdM * n;
714 double dotu = dotv + (-_Cuc * sin2u0 + _Cus * cos2u0) * 2 * dotv;
715 double dotom = _OMEGADOT - omegaEarth;
716 double doti = _IDOT + (-_Cic * sin2u0 + _Cis * cos2u0) * 2 * dotv;
717 double dotr = a0 * _e * sin(E) * dEdM * n
718 + (-_Crc * sin2u0 + _Crs * cos2u0) * 2 * dotv;
719 double dotx = dotr * cos(u) - r * sin(u) * dotu;
720 double doty = dotr * sin(u) + r * cos(u) * dotu;
721
722 vv[0] = cosom * dotx - cosi * sinom * doty // dX / dr
723 - xp * sinom * dotom - yp * cosi * cosom * dotom // dX / dOMEGA
724 + yp * sini * sinom * doti; // dX / di
725
726 vv[1] = sinom * dotx + cosi * cosom * doty + xp * cosom * dotom
727 - yp * cosi * sinom * dotom - yp * sini * cosom * doti;
728
729 vv[2] = sini * doty + yp * cosi * doti;
730
731 // Relativistic Correction
732 // -----------------------
733 xc[3] -= 4.442807633e-10 * _e * sqrt(a0) * sin(E);
734
735 xc[4] = _clock_drift + _clock_driftrate * tc;
736 xc[5] = _clock_driftrate;
737
738 return success;
739}
740
741// RINEX Format String
742//////////////////////////////////////////////////////////////////////////////
743QString t_ephGPS::toString(double version) const {
744
745 if (version < 4.0 &&
746 (type() == t_eph::CNAV ||
747 type() == t_eph::CNV2 ||
748 type() == t_eph::L1NV )) {
749 return "";
750 }
751
752 QString ephStr = typeStr(_type, _prn, version);
753 QString rnxStr = ephStr + rinexDateStr(_TOC, _prn, version);
754
755 QTextStream out(&rnxStr);
756
757 out
758 << QString("%1%2%3\n")
759 .arg(_clock_bias, 19, 'e', 12)
760 .arg(_clock_drift, 19, 'e', 12)
761 .arg(_clock_driftrate, 19, 'e', 12);
762
763 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
764
765 // =====================
766 // BROADCAST ORBIT - 1
767 // =====================
768 if (type() == t_eph::CNAV ||
769 type() == t_eph::CNV2 ||
770 type() == t_eph::L1NV) {
771 out
772 << QString(fmt)
773 .arg(_ADOT, 19, 'e', 12)
774 .arg(_Crs, 19, 'e', 12)
775 .arg(_Delta_n, 19, 'e', 12)
776 .arg(_M0, 19, 'e', 12);
777 } else { // LNAV, undefined
778 out
779 << QString(fmt)
780 .arg(_IODE, 19, 'e', 12)
781 .arg(_Crs, 19, 'e', 12)
782 .arg(_Delta_n, 19, 'e', 12)
783 .arg(_M0, 19, 'e', 12);
784 }
785 // =====================
786 // BROADCAST ORBIT - 2
787 // =====================
788 out
789 << QString(fmt)
790 .arg(_Cuc, 19, 'e', 12)
791 .arg(_e, 19, 'e', 12)
792 .arg(_Cus, 19, 'e', 12)
793 .arg(_sqrt_A, 19, 'e', 12);
794 // =====================
795 // BROADCAST ORBIT - 3
796 // =====================
797 if (type() == t_eph::CNAV ||
798 type() == t_eph::CNV2) {
799 out
800 << QString(fmt)
801 .arg(_top, 19, 'e', 12)
802 .arg(_Cic, 19, 'e', 12)
803 .arg(_OMEGA0, 19, 'e', 12)
804 .arg(_Cis, 19, 'e', 12);
805 }
806 else if (type() == t_eph::L1NV) {
807 out
808 << QString(fmt)
809 .arg(_IODE, 19, 'e', 12)
810 .arg(_Cic, 19, 'e', 12)
811 .arg(_OMEGA0, 19, 'e', 12)
812 .arg(_Cis, 19, 'e', 12);
813 }
814 else { // LNAV, undefined
815 out
816 << QString(fmt)
817 .arg(_TOEsec, 19, 'e', 12)
818 .arg(_Cic, 19, 'e', 12)
819 .arg(_OMEGA0, 19, 'e', 12)
820 .arg(_Cis, 19, 'e', 12);
821 }
822 // =====================
823 // BROADCAST ORBIT - 4
824 // =====================
825 out
826 << QString(fmt)
827 .arg(_i0, 19, 'e', 12)
828 .arg(_Crc, 19, 'e', 12)
829 .arg(_omega, 19, 'e', 12)
830 .arg(_OMEGADOT, 19, 'e', 12);
831 // =====================
832 // BROADCAST ORBIT - 5
833 // =====================
834 if (system() != t_eph::NavIC) {
835 if (type() == t_eph::CNAV ||
836 type() == t_eph::CNV2) {
837 out
838 << QString(fmt)
839 .arg(_IDOT, 19, 'e', 12)
840 .arg(_Delta_n_dot, 19, 'e', 12)
841 .arg(_URAI_NED0, 19, 'e', 12)
842 .arg(_URAI_NED1, 19, 'e', 12);
843 }
844 else { // LNAV, undefined
845 out
846 << QString(fmt)
847 .arg(_IDOT, 19, 'e', 12)
848 .arg(_L2Codes, 19, 'e', 12)
849 .arg(_TOEweek, 19, 'e', 12)
850 .arg(_L2PFlag, 19, 'e', 12);
851 }
852 }
853 else {
854 if (type() == t_eph::LNAV ||
855 type() == t_eph::undefined) {
856 out
857 << QString(fmt)
858 .arg(_IDOT, 19, 'e', 12)
859 .arg("", 19, QChar(' '))
860 .arg(_TOEweek, 19, 'e', 12)
861 .arg("", 19, QChar(' '));
862 }
863 else if (type() == t_eph::L1NV) {
864 out
865 << QString(fmt)
866 .arg(_IDOT, 19, 'e', 12)
867 .arg(_Delta_n_dot, 19, 'e', 12)
868 .arg("", 19, QChar(' '))
869 .arg(_RSF, 19, 'e', 12);
870 }
871 }
872 // =====================
873 // BROADCAST ORBIT - 6
874 // =====================
875 if (system() != t_eph::NavIC) {
876 if (type() == t_eph::CNAV ||
877 type() == t_eph::CNV2) {
878 out
879 << QString(fmt)
880 .arg(_URAI_ED, 19, 'e', 12)
881 .arg(_health, 19, 'e', 12)
882 .arg(_TGD, 19, 'e', 12)
883 .arg(_URAI_NED2, 19, 'e', 12);
884 }
885 else { // LNAV, undefined
886 out
887 << QString(fmt)
888 .arg(_ura, 19, 'e', 12)
889 .arg(_health, 19, 'e', 12)
890 .arg(_TGD, 19, 'e', 12)
891 .arg(_IODC, 19, 'e', 12);
892 }
893 }
894 else {
895 if (type() == t_eph::LNAV ||
896 type() == t_eph::undefined) {
897 out
898 << QString(fmt)
899 .arg(_ura, 19, 'e', 12)
900 .arg(_health, 19, 'e', 12)
901 .arg(_TGD, 19, 'e', 12)
902 .arg("", 19, QChar(' '));
903 }
904 else if (type() == t_eph::L1NV) {
905 int i = 0; (!_RSF) ? i = 2 : i = 3;
906 if (i == 2) {
907 out
908 << QString(fmt)
909 .arg(_URAI, 19, 'e', 12)
910 .arg(_health, 19, 'e', 12)
911 .arg(_TGD, 19, 'e', 12)
912 .arg("", 19, QChar(' '));
913 }
914 else {
915 out
916 << QString(fmt)
917 .arg(_URAI, 19, 'e', 12)
918 .arg(_health, 19, 'e', 12)
919 .arg("", 19, QChar(' '))
920 .arg(_TGD, 19, 'e', 12);
921 }
922 }
923 }
924 // =====================
925 // BROADCAST ORBIT - 7
926 // =====================
927 if (type() == t_eph::LNAV ||
928 type() == t_eph::undefined) {
929
930 double tot = _TOT;
931 if (tot == 0.9999e9 && version < 3.0) {
932 tot = 0.0;
933 }
934 // fitInterval
935 if (system() == t_eph::NavIC) {
936 out
937 << QString(fmt)
938 .arg(tot, 19, 'e', 12)
939 .arg("", 19, QChar(' '))
940 .arg("", 19, QChar(' '))
941 .arg("", 19, QChar(' '));
942 }
943 else {
944 if (_flags_unknown) {
945 out
946 << QString(fmt)
947 .arg(tot, 19, 'e', 12)
948 .arg("", 19, QChar(' '))
949 .arg("", 19, QChar(' '))
950 .arg("", 19, QChar(' '));
951 }
952 else {
953 // for GPS and QZSS in version 3.02 specified in hours
954 double fitIntervalRnx = _fitInterval;
955 // otherwise specified as flag
956 if (system() == t_eph::QZSS && version != 3.02) {
957 (_fitInterval == 2.0) ? fitIntervalRnx = 0.0 : fitIntervalRnx = 1.0;
958 }
959 out
960 << QString(fmt)
961 .arg(tot, 19, 'e', 12)
962 .arg(fitIntervalRnx, 19, 'e', 12)
963 .arg("", 19, QChar(' '))
964 .arg("", 19, QChar(' '));
965 }
966 }
967 }
968 else if (type() == t_eph::CNAV ||
969 type() == t_eph::CNV2) {
970 out
971 << QString(fmt)
972 .arg(_ISC_L1CA, 19, 'e', 12)
973 .arg(_ISC_L2C, 19, 'e', 12)
974 .arg(_ISC_L5I5, 19, 'e', 12)
975 .arg(_ISC_L5Q5, 19, 'e', 12);
976 }
977 else if (type() == t_eph::L1NV) {
978 if (_RSF) {
979 out
980 << QString(fmt)
981 .arg(_ISC_S, 19, 'e', 12)
982 .arg(_ISC_L1D, 19, 'e', 12)
983 .arg("", 19, QChar(' '))
984 .arg("", 19, QChar(' '));
985 }
986 else {
987 out
988 << QString(fmt)
989 .arg("", 19, QChar(' '))
990 .arg("", 19, QChar(' '))
991 .arg(_ISC_L1P, 19, 'e', 12)
992 .arg(_ISC_L1D, 19, 'e', 12);
993 }
994 }
995 // =====================
996 // BROADCAST ORBIT - 8
997 // =====================
998 if (type() == t_eph::CNAV) {
999 int intFlags = 0;
1000 if (!_flags_unknown) {
1001 // Bit 0:
1002 if (_intSF) {intFlags |= (1 << 0);}
1003 // Bit 1:
1004 if (_L2Cphasing) {intFlags |= (1 << 1);}
1005 // Bit 2:
1006 if (_alert) {intFlags |= (1 << 2);}
1007 out
1008 << QString(fmt)
1009 .arg(_TOT, 19, 'e', 12)
1010 .arg(_wnop, 19, 'e', 12)
1011 .arg(double(intFlags), 19, 'e', 12)
1012 .arg("", 19, QChar(' '));
1013 }
1014 else {
1015 out
1016 << QString(fmt)
1017 .arg(_TOT, 19, 'e', 12)
1018 .arg(_wnop, 19, 'e', 12)
1019 .arg("", 19, QChar(' '))
1020 .arg("", 19, QChar(' '));
1021 }
1022 }
1023 else if (type() == t_eph::CNV2) {
1024 out
1025 << QString(fmt)
1026 .arg(_ISC_L1Cd, 19, 'e', 12)
1027 .arg(_ISC_L1Cp, 19, 'e', 12)
1028 .arg("", 19, QChar(' '))
1029 .arg("", 19, QChar(' '));
1030 }
1031 else if (type() == t_eph::L1NV) {
1032 out
1033 << QString(fmt)
1034 .arg(_TOT, 19, 'e', 12)
1035 .arg("", 19, QChar(' '))
1036 .arg("", 19, QChar(' '))
1037 .arg("", 19, QChar(' '));
1038 }
1039 // =====================
1040 // BROADCAST ORBIT - 9
1041 // =====================
1042 if (type() == t_eph::CNV2) {
1043 int intFlags = 0;
1044 if (!_flags_unknown) {
1045 // Bit 0:
1046 if (_intSF) {intFlags |= (1 << 0);}
1047 if (system() == t_eph::QZSS) {
1048 // Bit 1:
1049 if (_ephSF) {intFlags |= (1 << 1);}
1050 }
1051 out
1052 << QString(fmt)
1053 .arg(_TOT, 19, 'e', 12)
1054 .arg(_wnop, 19, 'e', 12)
1055 .arg(double(intFlags), 19, 'e', 12)
1056 .arg("", 19, QChar(' '));
1057 }
1058 else {
1059 out
1060 << QString(fmt)
1061 .arg(_TOT, 19, 'e', 12)
1062 .arg(_wnop, 19, 'e', 12)
1063 .arg("", 19, QChar(' '))
1064 .arg("", 19, QChar(' '));
1065 }
1066 }
1067 return rnxStr;
1068}
1069
1070// Health status of GPS Ephemeris (virtual)
1071////////////////////////////////////////////////////////////////////////////
1072unsigned int t_ephGPS::isUnhealthy() const {
1073
1074 switch (system()) {
1075 case t_eph::GPS:
1076 case t_eph::QZSS:
1077 switch (type()) {
1078 case t_eph::LNAV:
1079 case t_eph::CNAV:
1080 case t_eph::CNV2:
1081 if (_health == 0.0) {
1082 return 0;
1083 }
1084 else {
1085 return 1;
1086 }
1087 break;
1088 }
1089 break;
1090 case t_eph::NavIC:
1091 switch (type()) {
1092 case t_eph::LNAV:
1093 if (_health == 0.0) { // L1 & S healthy
1094 return 0;
1095 }
1096 if (_health == 1.0 || // L5 healthy and S unhealthy,
1097 _health == 2.0 || // L5 unhealthy and S healthy
1098 _health == 3.0) { // both L5 and S unhealthy
1099 return 1;
1100 }
1101 break;
1102 case t_eph::L1NV:
1103 if (_health == 0.0) { // All navigation data on L1-SPS signal OK
1104 return 0;
1105 }
1106 if (_health == 1.0) { // Some or all navigation data on L1-SPS signal bad
1107 return 1;
1108 }
1109 break;
1110 }
1111 break;
1112 }
1113 return 0;
1114}
1115
1116// Constructor
1117//////////////////////////////////////////////////////////////////////////////
1118t_ephGlo::t_ephGlo(double rnxVersion, const QStringList &lines, const QString typeStr) {
1119
1120 setType(typeStr);
1121
1122 int nLines = 4;
1123
1124 // Source RINEX version < 4
1125 if (type() == t_eph::undefined) {
1126 // cannot be determined from input data
1127 // but is set to be able to work with old RINEX files in BNC applications
1128 _type = t_eph::FDMA_M;
1129 }
1130
1131 if (rnxVersion >= 3.05) {
1132 nLines += 1;
1133 } else {
1134 _M_delta_tau = 0.9999e9; // unknown
1135 _M_FT = 1.5e1; // unknown
1136 _statusflags_unknown = true;
1137 _healthflags_unknown = true;
1138 }
1139
1140 if (lines.size() != nLines) {
1141 _checkState = bad;
1142 return;
1143 }
1144
1145 // RINEX Format
1146 // ------------
1147 int fieldLen = 19;
1148 double statusflags = 0.0;
1149 double healthflags = 0.0;
1150 double sourceflags = 0.0;
1151 _tauC = 0.0;
1152 _tau1 = 0.0;
1153 _tau2 = 0.0;
1154 _additional_data_availability = 0.0;
1155
1156 int pos[4];
1157 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
1158 pos[1] = pos[0] + fieldLen;
1159 pos[2] = pos[1] + fieldLen;
1160 pos[3] = pos[2] + fieldLen;
1161
1162 // Read four lines
1163 // ---------------
1164 for (int iLine = 0; iLine < nLines; iLine++) {
1165 QString line = lines[iLine];
1166
1167 if (iLine == 0) {
1168 QTextStream in(line.left(pos[1]).toLatin1());
1169
1170 int year, month, day, hour, min;
1171 double sec;
1172
1173 QString prnStr, n;
1174 in >> prnStr;
1175 if (prnStr.size() == 1 && prnStr[0] == 'R') {
1176 in >> n;
1177 prnStr.append(n);
1178 }
1179
1180 if (prnStr.at(0) == 'R') {
1181 _prn.set('R', prnStr.mid(1).toInt());
1182 } else {
1183 _prn.set('R', prnStr.toInt());
1184 }
1185
1186 in >> year >> month >> day >> hour >> min >> sec;
1187 if (year < 80) {
1188 year += 2000;
1189 } else if (year < 100) {
1190 year += 1900;
1191 }
1192
1193 _gps_utc = gnumleap(year, month, day);
1194
1195 _TOC.set(year, month, day, hour, min, sec);
1196 _TOC = _TOC + _gps_utc;
1197 int nd = int((_TOC.gpssec())) / (24.0 * 60.0 * 60.0);
1198 if ( readDbl(line, pos[1], fieldLen, _tau)
1199 || readDbl(line, pos[2], fieldLen, _gamma)
1200 || readDbl(line, pos[3], fieldLen, _tki)) {
1201 _checkState = bad;
1202 return;
1203 }
1204 _tki -= nd * 86400.0;
1205 _tau = -_tau;
1206 }
1207 // =====================
1208 // BROADCAST ORBIT - 1
1209 // =====================
1210 else if (iLine == 1) {
1211 if ( readDbl(line, pos[0], fieldLen, _x_pos)
1212 || readDbl(line, pos[1], fieldLen, _x_vel)
1213 || readDbl(line, pos[2], fieldLen, _x_acc)
1214 || readDbl(line, pos[3], fieldLen, _health)) {
1215 _checkState = bad;
1216 return;
1217 }
1218 }
1219 // =====================
1220 // BROADCAST ORBIT - 2
1221 // =====================
1222 else if (iLine == 2) {
1223 if (type() == t_eph::FDMA ||
1224 type() == t_eph::FDMA_M) {
1225 if ( readDbl(line, pos[0], fieldLen, _y_pos)
1226 || readDbl(line, pos[1], fieldLen, _y_vel)
1227 || readDbl(line, pos[2], fieldLen, _y_acc)
1228 || readDbl(line, pos[3], fieldLen, _frq_num)) {
1229 _checkState = bad;
1230 return;
1231 }
1232 }
1233 else { //L1OC, L3OC
1234 if ( readDbl(line, pos[0], fieldLen, _y_pos)
1235 || readDbl(line, pos[1], fieldLen, _y_vel)
1236 || readDbl(line, pos[2], fieldLen, _y_acc)
1237 || readDbl(line, pos[3], fieldLen, statusflags)) {
1238 _checkState = bad;
1239 return;
1240 }
1241 _data_validity = int(statusflags);
1242 }
1243 }
1244 // =====================
1245 // BROADCAST ORBIT - 3
1246 // =====================
1247 else if (iLine == 3) {
1248 if (type() == t_eph::FDMA ||
1249 type() == t_eph::FDMA_M) {
1250 if ( readDbl(line, pos[0], fieldLen, _z_pos)
1251 || readDbl(line, pos[1], fieldLen, _z_vel)
1252 || readDbl(line, pos[2], fieldLen, _z_acc)
1253 || readDbl(line, pos[3], fieldLen, _E)) {
1254 _checkState = bad;
1255 return;
1256 }
1257 }
1258 else if (type() == t_eph::L1OC) {
1259 if ( readDbl(line, pos[0], fieldLen, _z_pos)
1260 || readDbl(line, pos[1], fieldLen, _z_vel)
1261 || readDbl(line, pos[2], fieldLen, _z_acc)
1262 || readDbl(line, pos[3], fieldLen, _TGD_L2OCp)) {
1263 _checkState = bad;
1264 return;
1265 }
1266 }
1267 else if (type() == t_eph::L3OC) {
1268 if ( readDbl(line, pos[0], fieldLen, _z_pos)
1269 || readDbl(line, pos[1], fieldLen, _z_vel)
1270 || readDbl(line, pos[2], fieldLen, _z_acc)
1271 || readDbl(line, pos[3], fieldLen, _TGD_L3OCp)) {
1272 _checkState = bad;
1273 return;
1274 }
1275 }
1276 }
1277 // =====================
1278 // BROADCAST ORBIT - 4
1279 // =====================
1280 else if (iLine == 4) {
1281 if (type() == t_eph::FDMA ||
1282 type() == t_eph::FDMA_M) {
1283 if (readDbl(line, pos[0], fieldLen, statusflags)) {
1284 //status flags BLK, do nothing
1285 _statusflags_unknown = true;
1286 } else {
1287 _statusflags_unknown = false;
1288 // status flags
1289 // ============
1290 // bit 0-1
1291 _M_P = double(bitExtracted(unsigned(statusflags), 2, 0));
1292 // bit 2-3
1293 _P1 = double(bitExtracted(unsigned(statusflags), 2, 2));
1294 // bit 4
1295 _P2 = double(bitExtracted(unsigned(statusflags), 1, 4));
1296 // bit 5
1297 _P3 = double(bitExtracted(unsigned(statusflags), 1, 5));
1298 // bit 6
1299 _M_P4 = double(bitExtracted(unsigned(statusflags), 1, 6));
1300 // bit 7-8
1301 _M_M = double(bitExtracted(unsigned(statusflags), 2, 7));
1302 /// GLO M/K exclusive flags/values only valid if flag M is set to '01'
1303 if (!_M_M) {
1304 _M_P = 0.0;
1305 _M_l3 = 0.0;
1306 _M_P4 = 0.0;
1307 _M_FE = 0.0;
1308 _M_FT = 0.0;
1309 _M_NA = 0.0;
1310 _M_NT = 0.0;
1311 _M_N4 = 0.0;
1312 _M_l5 = 0.0;
1313 _M_tau_GPS = 0.0;
1314 _M_delta_tau = 0.0;
1315 _type = t_eph::FDMA;
1316 }
1317 else {
1318 _type = t_eph::FDMA_M;
1319 }
1320 }
1321 if ( readDbl(line, pos[1], fieldLen, _M_delta_tau)
1322 || readDbl(line, pos[2], fieldLen, _M_FT)) {
1323 _checkState = bad;
1324 return;
1325 }
1326 if (readDbl(line, pos[3], fieldLen, healthflags)) {
1327 // health flags BLK
1328 _healthflags_unknown = true;
1329 } else {
1330 _healthflags_unknown = false;
1331 // health flags
1332 // ============
1333 // bit 0 (is to be ignored, if bit 1 is zero)
1334 _almanac_health = double(bitExtracted(unsigned(healthflags), 1, 0));
1335 // bit 1
1336 _almanac_health_availablility_indicator =
1337 double(bitExtracted(unsigned(healthflags), 1, 1));
1338 // bit 2; GLO-M/K only, health bit of string 3
1339 _M_l3 = double(bitExtracted(unsigned(healthflags), 1, 2));
1340 }
1341 }
1342 else if (type() == t_eph::L1OC ||
1343 type() == t_eph::L3OC) {
1344 if ( readDbl(line, pos[0], fieldLen, _sat_type)
1345 || readDbl(line, pos[1], fieldLen, sourceflags)
1346 || readDbl(line, pos[2], fieldLen, _EE)
1347 || readDbl(line, pos[3], fieldLen, _ET)) {
1348 _checkState = bad;
1349 return;
1350 }
1351 // sourceflags:
1352 // ============
1353 // bit 0-1
1354 _RT = double(bitExtracted(unsigned(sourceflags), 2, 0));
1355 // bit 2-3:
1356 _RE = double(bitExtracted(unsigned(sourceflags), 2, 2));
1357 }
1358 }
1359 // =====================
1360 // BROADCAST ORBIT - 5
1361 // =====================
1362 else if (iLine == 5) {
1363 if (type() == t_eph::L1OC ||
1364 type() == t_eph::L3OC) {
1365 if ( readDbl(line, pos[0], fieldLen, _attitude_P2)
1366 || readDbl(line, pos[1], fieldLen, _Tin)
1367 || readDbl(line, pos[2], fieldLen, _tau1)
1368 || readDbl(line, pos[3], fieldLen, _tau2)) {
1369 _checkState = bad;
1370 return;
1371 }
1372 }
1373 }
1374 // =====================
1375 // BROADCAST ORBIT - 6
1376 // =====================
1377 else if (iLine == 6) {
1378 if (type() == t_eph::L1OC ||
1379 type() == t_eph::L3OC) {
1380 if ( readDbl(line, pos[0], fieldLen, _yaw)
1381 || readDbl(line, pos[1], fieldLen, _sn)
1382 || readDbl(line, pos[2], fieldLen, _angular_rate)
1383 || readDbl(line, pos[3], fieldLen, _angular_acc)) {
1384 _checkState = bad;
1385 return;
1386 }
1387 }
1388 }
1389 // =====================
1390 // BROADCAST ORBIT - 7
1391 // =====================
1392 else if (iLine == 7) {
1393 if (type() == t_eph::L1OC ||
1394 type() == t_eph::L3OC) {
1395 if ( readDbl(line, pos[0], fieldLen, _angular_rate_max)
1396 || readDbl(line, pos[1], fieldLen, _X_PC)
1397 || readDbl(line, pos[2], fieldLen, _Y_PC)
1398 || readDbl(line, pos[3], fieldLen, _Z_PC)) {
1399 _checkState = bad;
1400 return;
1401 }
1402 }
1403 }
1404 // =====================
1405 // BROADCAST ORBIT - 8
1406 // =====================
1407 else if (iLine == 8) {
1408 if (type() == t_eph::L1OC ||
1409 type() == t_eph::L3OC) {
1410 if ( readDbl(line, pos[0], fieldLen, _M_FE)
1411 || readDbl(line, pos[1], fieldLen, _M_FT)
1412 || readDbl(line, pos[3], fieldLen, _TOT)) {
1413 _checkState = bad;
1414 return;
1415 }
1416 }
1417 }
1418 }
1419
1420 _prn.setFlag(type());
1421
1422 // Initialize status vector
1423 // ------------------------
1424 _tt = _TOC;
1425 _xv.ReSize(6);
1426 _xv = 0.0;
1427 _xv(1) = _x_pos * 1.e3;
1428 _xv(2) = _y_pos * 1.e3;
1429 _xv(3) = _z_pos * 1.e3;
1430 _xv(4) = _x_vel * 1.e3;
1431 _xv(5) = _y_vel * 1.e3;
1432 _xv(6) = _z_vel * 1.e3;
1433}
1434
1435// Compute Glonass Satellite Position (virtual)
1436////////////////////////////////////////////////////////////////////////////
1437t_irc t_ephGlo::position(int GPSweek, double GPSweeks, double *xc,
1438 double *vv) const {
1439
1440 static const double nominalStep = 10.0;
1441
1442 memset(xc, 0, 6 * sizeof(double));
1443 memset(vv, 0, 3 * sizeof(double));
1444
1445 bncTime tt(GPSweek, GPSweeks);
1446 double dtPos = tt - _TOC;
1447
1448 if (fabs(dtPos) > 24 * 3600.0) {
1449 return failure;
1450 }
1451
1452 int nSteps = int(fabs(dtPos) / nominalStep) + 1;
1453 double step = dtPos / nSteps;
1454
1455 double acc[3];
1456 acc[0] = _x_acc * 1.e3;
1457 acc[1] = _y_acc * 1.e3;
1458 acc[2] = _z_acc * 1.e3;
1459
1460 // Always integrate fresh from the pristine, decoded TOC state rather
1461 // than rolling _tt/_xv forward across repeated calls. position() can be
1462 // called very frequently (multiple times per epoch, from multiple call
1463 // sites) - chaining many short integration steps across those calls,
1464 // instead of one direct integration to the requested time, lets small
1465 // per-call numerical error accumulate over wall-clock time independent
1466 // of how far the request actually is from TOC, producing a slow but
1467 // steady drift (observed: ~0.5-0.7 mm/s along-track for a GLONASS
1468 // satellite whose broadcast message was barely a few minutes old).
1469 // ----------------------------------------------------------------
1470 ColumnVector xv(6);
1471 xv(1) = _x_pos * 1.e3;
1472 xv(2) = _y_pos * 1.e3;
1473 xv(3) = _z_pos * 1.e3;
1474 xv(4) = _x_vel * 1.e3;
1475 xv(5) = _y_vel * 1.e3;
1476 xv(6) = _z_vel * 1.e3;
1477
1478 bncTime ttLocal = _TOC;
1479 for (int ii = 1; ii <= nSteps; ii++) {
1480 xv = rungeKutta4(ttLocal.gpssec(), xv, step, acc, glo_deriv);
1481 ttLocal = ttLocal + step;
1482 }
1483
1484 // Position and Velocity
1485 // ---------------------
1486 xc[0] = xv(1);
1487 xc[1] = xv(2);
1488 xc[2] = xv(3);
1489
1490 vv[0] = xv(4);
1491 vv[1] = xv(5);
1492 vv[2] = xv(6);
1493
1494 // Clock Correction
1495 // ----------------
1496 double dtClk = tt - _TOC;
1497 xc[3] = -_tau + _gamma * dtClk;
1498
1499 xc[4] = _gamma;
1500 xc[5] = 0.0;
1501
1502 return success;
1503}
1504
1505// RINEX Format String
1506//////////////////////////////////////////////////////////////////////////////
1507QString t_ephGlo::toString(double version) const {
1508
1509 if (version < 4.0 &&
1510 (type() == t_eph::L1OC ||
1511 type() == t_eph::L3OC )) {
1512 return "";
1513 }
1514
1515 QString ephStr = typeStr(_type, _prn, version);
1516 QString rnxStr = ephStr + rinexDateStr(_TOC - _gps_utc, _prn, version);
1517 int nd = int((_TOC - _gps_utc).gpssec()) / (24.0 * 60.0 * 60.0);
1518 QTextStream out(&rnxStr);
1519
1520 out
1521 << QString("%1%2%3\n")
1522 .arg(-_tau, 19, 'e', 12)
1523 .arg(_gamma, 19, 'e', 12)
1524 .arg(_tki + nd * 86400.0, 19, 'e', 12);
1525
1526 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
1527 // =====================
1528 // BROADCAST ORBIT - 1
1529 // =====================
1530 out
1531 << QString(fmt)
1532 .arg(_x_pos, 19, 'e', 12)
1533 .arg(_x_vel, 19, 'e', 12)
1534 .arg(_x_acc, 19, 'e', 12)
1535 .arg(_health, 19, 'e', 12);
1536
1537 // =====================
1538 // BROADCAST ORBIT - 2
1539 // =====================
1540 if (type() == t_eph::FDMA ||
1541 type() == t_eph::FDMA_M) {
1542 out
1543 << QString(fmt)
1544 .arg(_y_pos, 19, 'e', 12)
1545 .arg(_y_vel, 19, 'e', 12)
1546 .arg(_y_acc, 19, 'e', 12)
1547 .arg(_frq_num, 19, 'e', 12);
1548 }
1549 else { //L1OC, L3OC
1550 out
1551 << QString(fmt)
1552 .arg(_y_pos, 19, 'e', 12)
1553 .arg(_y_vel, 19, 'e', 12)
1554 .arg(_y_acc, 19, 'e', 12)
1555 .arg(double(_data_validity), 19, 'e', 12);
1556 }
1557 // =====================
1558 // BROADCAST ORBIT - 3
1559 // =====================
1560 if (type() == t_eph::FDMA ||
1561 type() == t_eph::FDMA_M) {
1562 out
1563 << QString(fmt)
1564 .arg(_z_pos, 19, 'e', 12)
1565 .arg(_z_vel, 19, 'e', 12)
1566 .arg(_z_acc, 19, 'e', 12)
1567 .arg(_E, 19, 'e', 12);
1568 }
1569 else if (type() == t_eph::L1OC) {
1570 out
1571 << QString(fmt)
1572 .arg(_z_pos, 19, 'e', 12)
1573 .arg(_z_vel, 19, 'e', 12)
1574 .arg(_z_acc, 19, 'e', 12)
1575 .arg(_TGD_L2OCp, 19, 'e', 12);
1576 }
1577 else if (type() == t_eph::L3OC) {
1578 out
1579 << QString(fmt)
1580 .arg(_z_pos, 19, 'e', 12)
1581 .arg(_z_vel, 19, 'e', 12)
1582 .arg(_z_acc, 19, 'e', 12)
1583 .arg(_TGD_L3OCp, 19, 'e', 12);
1584 }
1585 if (version >= 3.05) {
1586 // =====================
1587 // BROADCAST ORBIT - 4
1588 // =====================
1589 if (type() == t_eph::FDMA ||
1590 type() == t_eph::FDMA_M){
1591 int statusflags = 0;
1592 int healthflags = 0;
1593 if (!_statusflags_unknown ) {
1594 // bit 0-1
1595 if (_M_P == 1.0) {statusflags |= (1 << 0);}
1596 else if (_M_P == 2.0) {statusflags |= (1 << 1);}
1597 else if (_M_P == 3.0) {statusflags |= (1 << 0); statusflags |= (1 << 1);}
1598 // bit 2-3
1599 if (_P1 == 1.0) {statusflags |= (1 << 2);}
1600 else if (_P1 == 2.0) {statusflags |= (1 << 3);}
1601 else if (_P1 == 3.0) {statusflags |= (1 << 2); statusflags |= (1 << 3);}
1602 // bit 4
1603 if (_P2) {statusflags |= (1 << 4);}
1604 // bit 5
1605 if (_P3) {statusflags |= (1 << 5);}
1606 // bit 6
1607 if (_M_P4) {statusflags |= (1 << 6);}
1608 // bit 7-8
1609 if (_M_M == 1.0) {statusflags |= (1 << 7);}
1610 }
1611 if (!_healthflags_unknown) {
1612 // bit 0 (is to be ignored, if bit 1 is zero)
1613 if (_almanac_health) {healthflags |= (1 << 0);}
1614 // bit 1
1615 if (_almanac_health_availablility_indicator) {healthflags |= (1 << 1);}
1616 // bit 2
1617 if (_M_l3) {healthflags |= (1 << 2);}
1618 }
1619
1620 if (_statusflags_unknown && _healthflags_unknown) {
1621 out
1622 << QString(fmt)
1623 .arg("", 19, QChar(' ')) // status-flags BNK (unknown)
1624 .arg(_M_delta_tau, 19, 'e', 12)
1625 .arg(_M_FT, 19, 'e', 12)
1626 .arg("", 19, QChar(' '));// health-flags BNK (unknown)
1627 }
1628 else if (!_statusflags_unknown && _healthflags_unknown) {
1629 out
1630 << QString(fmt)
1631 .arg(double(statusflags), 19, 'e', 12)
1632 .arg(_M_delta_tau, 19, 'e', 12)
1633 .arg(_M_FT, 19, 'e', 12)
1634 .arg("", 19, QChar(' '));// health-flags BNK (unknown)
1635 }
1636 else if (_statusflags_unknown && !_healthflags_unknown) {
1637 out
1638 << QString(fmt)
1639 .arg("", 19, QChar(' ')) // status-flags BNK (unknown)
1640 .arg(_M_delta_tau, 19, 'e', 12)
1641 .arg(_M_FT, 19, 'e', 12)
1642 .arg(double(healthflags), 19, 'e', 12);
1643 }
1644 else if (!_statusflags_unknown && !_healthflags_unknown) {
1645 out
1646 << QString(fmt)
1647 .arg(double(statusflags), 19, 'e', 12)
1648 .arg(_M_delta_tau, 19, 'e', 12)
1649 .arg(_M_FT, 19, 'e', 12)
1650 .arg(double(healthflags), 19, 'e', 12);
1651 }
1652 }
1653 else if (type() == t_eph::L1OC ||
1654 type() == t_eph::L3OC) {
1655 int sourceflags = 0;
1656 // bit 0-1
1657 if (_RT == 1.0) {sourceflags |= (1 << 0);}
1658 else if (_RT == 2.0) {sourceflags |= (1 << 1);}
1659 else if (_RT == 3.0) {sourceflags |= (1 << 0); sourceflags |= (1 << 1);}
1660 // bit 2-3
1661 if (_RE == 1.0) {sourceflags |= (1 << 2);}
1662 else if (_RE == 2.0) {sourceflags |= (1 << 3);}
1663 else if (_RE == 3.0) {sourceflags |= (1 << 2); sourceflags |= (1 << 3);}
1664 out
1665 << QString(fmt)
1666 .arg(_sat_type , 19, 'e', 12)
1667 .arg(double(sourceflags), 19, 'e', 12)
1668 .arg(_ET, 19, 'e', 12)
1669 .arg(_EE, 19, 'e', 12);
1670 }
1671 // =====================
1672 // BROADCAST ORBIT - 5
1673 // =====================
1674 if (type() == t_eph::L1OC ||
1675 type() == t_eph::L3OC) {
1676 out
1677 << QString(fmt)
1678 .arg(_attitude_P2, 19, 'e', 12)
1679 .arg(_Tin, 19, 'e', 12)
1680 .arg(_tau1, 19, 'e', 12)
1681 .arg(_tau2, 19, 'e', 12);
1682 }
1683 // =====================
1684 // BROADCAST ORBIT - 6
1685 // =====================
1686 if (type() == t_eph::L1OC ||
1687 type() == t_eph::L3OC) {
1688 out
1689 << QString(fmt)
1690 .arg(_yaw, 19, 'e', 12)
1691 .arg(_sn, 19, 'e', 12)
1692 .arg(_angular_rate, 19, 'e', 12)
1693 .arg(_angular_acc, 19, 'e', 12);
1694 }
1695 // =====================
1696 // BROADCAST ORBIT - 7
1697 // =====================
1698 if (type() == t_eph::L1OC ||
1699 type() == t_eph::L3OC) {
1700 out
1701 << QString(fmt)
1702 .arg(_angular_rate_max, 19, 'e', 12)
1703 .arg(_X_PC, 19, 'e', 12)
1704 .arg(_Y_PC, 19, 'e', 12)
1705 .arg(_Z_PC, 19, 'e', 12);
1706 }
1707 // =====================
1708 // BROADCAST ORBIT - 8
1709 // =====================
1710 if (type() == t_eph::L1OC ||
1711 type() == t_eph::L3OC) {
1712 out
1713 << QString(fmt)
1714 .arg(_M_FE, 19, 'e', 12)
1715 .arg(_M_FT, 19, 'e', 12)
1716 .arg("", 19, QChar(' '))
1717 .arg(_TOT, 19, 'e', 12);
1718 }
1719 }
1720 return rnxStr;
1721}
1722
1723// Derivative of the state vector using a simple force model (static)
1724////////////////////////////////////////////////////////////////////////////
1725ColumnVector t_ephGlo::glo_deriv(double /* tt */, const ColumnVector &xv,
1726 double *acc) {
1727
1728 // State vector components
1729 // -----------------------
1730 ColumnVector rr = xv.rows(1, 3);
1731 ColumnVector vv = xv.rows(4, 6);
1732
1733 // Acceleration
1734 // ------------
1735 static const double gmWGS = 398.60044e12;
1736 static const double AE = 6378136.0;
1737 static const double OMEGA = 7292115.e-11;
1738 static const double C20 = -1082.6257e-6;
1739
1740 double rho = rr.NormFrobenius();
1741 double t1 = -gmWGS / (rho * rho * rho);
1742 double t2 = 3.0 / 2.0 * C20 * (gmWGS * AE * AE)
1743 / (rho * rho * rho * rho * rho);
1744 double t3 = OMEGA * OMEGA;
1745 double t4 = 2.0 * OMEGA;
1746 double z2 = rr(3) * rr(3);
1747
1748 // Vector of derivatives
1749 // ---------------------
1750 ColumnVector va(6);
1751 va(1) = vv(1);
1752 va(2) = vv(2);
1753 va(3) = vv(3);
1754 va(4) = (t1 + t2 * (1.0 - 5.0 * z2 / (rho * rho)) + t3) * rr(1) + t4 * vv(2)
1755 + acc[0];
1756 va(5) = (t1 + t2 * (1.0 - 5.0 * z2 / (rho * rho)) + t3) * rr(2) - t4 * vv(1)
1757 + acc[1];
1758 va(6) = (t1 + t2 * (3.0 - 5.0 * z2 / (rho * rho))) * rr(3) + acc[2];
1759
1760 return va;
1761}
1762
1763// IOD of Glonass Ephemeris (virtual)
1764////////////////////////////////////////////////////////////////////////////
1765unsigned int t_ephGlo::IOD() const {
1766 bncTime tMoscow = _TOC - _gps_utc + 3 * 3600.0;
1767 return (unsigned long) tMoscow.daysec() / 900;
1768}
1769
1770// Health status of Glonass Ephemeris (virtual)
1771////////////////////////////////////////////////////////////////////////////
1772unsigned int t_ephGlo::isUnhealthy() const {
1773
1774 if (_almanac_health_availablility_indicator) {
1775 if ((_health == 0 && _almanac_health == 0)
1776 || (_health == 1 && _almanac_health == 0)
1777 || (_health == 1 && _almanac_health == 1)) {
1778 return 1;
1779 }
1780 } else if (!_almanac_health_availablility_indicator) {
1781 if (_health) {
1782 return 1;
1783 }
1784 }
1785 return 0; /* (_health == 0 && _almanac_health == 1) or (_health == 0) */
1786}
1787
1788// Constructor
1789//////////////////////////////////////////////////////////////////////////////
1790t_ephGal::t_ephGal(double rnxVersion, const QStringList &lines, const QString typeStr) {
1791
1792 setType(typeStr);
1793
1794 const int nLines = 8;
1795
1796 if (lines.size() != nLines) {
1797 _checkState = bad;
1798 return;
1799 }
1800
1801 // RINEX Format
1802 // ------------
1803 int fieldLen = 19;
1804 double SVhealth = 0.0;
1805 double datasource = 0.0;
1806
1807 int pos[4];
1808 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
1809 pos[1] = pos[0] + fieldLen;
1810 pos[2] = pos[1] + fieldLen;
1811 pos[3] = pos[2] + fieldLen;
1812
1813 // Read eight lines
1814 // ----------------
1815 for (int iLine = 0; iLine < nLines; iLine++) {
1816 QString line = lines[iLine];
1817
1818 if (iLine == 0) {
1819 QTextStream in(line.left(pos[1]).toLatin1());
1820
1821 int year, month, day, hour, min;
1822 double sec;
1823
1824 QString prnStr, n;
1825 in >> prnStr;
1826 if (prnStr.size() == 1 && prnStr[0] == 'E') {
1827 in >> n;
1828 prnStr.append(n);
1829 }
1830 if (prnStr.at(0) == 'E') {
1831 _prn.set('E', prnStr.mid(1).toInt());
1832 } else {
1833 _prn.set('E', prnStr.toInt());
1834 }
1835
1836 in >> year >> month >> day >> hour >> min >> sec;
1837 if (year < 80) {
1838 year += 2000;
1839 } else if (year < 100) {
1840 year += 1900;
1841 }
1842
1843 _TOC.set(year, month, day, hour, min, sec);
1844
1845 if ( readDbl(line, pos[1], fieldLen, _clock_bias)
1846 || readDbl(line, pos[2], fieldLen, _clock_drift)
1847 || readDbl(line, pos[3], fieldLen, _clock_driftrate)) {
1848 _checkState = bad;
1849 return;
1850 }
1851 }
1852 // =====================
1853 // BROADCAST ORBIT - 1
1854 // =====================
1855 else if (iLine == 1) {
1856 if ( readDbl(line, pos[0], fieldLen, _IODnav)
1857 || readDbl(line, pos[1], fieldLen, _Crs)
1858 || readDbl(line, pos[2], fieldLen, _Delta_n)
1859 || readDbl(line, pos[3], fieldLen, _M0)) {
1860 _checkState = bad;
1861 return;
1862 }
1863 }
1864 // =====================
1865 // BROADCAST ORBIT - 2
1866 // =====================
1867 else if (iLine == 2) {
1868 if ( readDbl(line, pos[0], fieldLen, _Cuc)
1869 || readDbl(line, pos[1], fieldLen, _e)
1870 || readDbl(line, pos[2], fieldLen, _Cus)
1871 || readDbl(line, pos[3], fieldLen, _sqrt_A)) {
1872 _checkState = bad;
1873 return;
1874 }
1875 }
1876 // =====================
1877 // BROADCAST ORBIT - 3
1878 // =====================
1879 else if (iLine == 3) {
1880 if ( readDbl(line, pos[0], fieldLen, _TOEsec)
1881 || readDbl(line, pos[1], fieldLen, _Cic)
1882 || readDbl(line, pos[2], fieldLen, _OMEGA0)
1883 || readDbl(line, pos[3], fieldLen, _Cis)) {
1884 _checkState = bad;
1885 return;
1886 }
1887 }
1888 // =====================
1889 // BROADCAST ORBIT - 4
1890 // =====================
1891 else if (iLine == 4) {
1892 if ( readDbl(line, pos[0], fieldLen, _i0)
1893 || readDbl(line, pos[1], fieldLen, _Crc)
1894 || readDbl(line, pos[2], fieldLen, _omega)
1895 || readDbl(line, pos[3], fieldLen, _OMEGADOT)) {
1896 _checkState = bad;
1897 return;
1898 }
1899 }
1900 // =====================
1901 // BROADCAST ORBIT - 5
1902 // =====================
1903 else if (iLine == 5) {
1904 if ( readDbl(line, pos[0], fieldLen, _IDOT)
1905 || readDbl(line, pos[1], fieldLen, datasource)
1906 || readDbl(line, pos[2], fieldLen, _TOEweek)) {
1907 _checkState = bad;
1908 return;
1909 }
1910 else {
1911 if (bitExtracted(unsigned(datasource), 1, 8)) {
1912 _fnav = true;
1913 _type = t_eph::FNAV;
1914 _inav = false;
1915 /* set unused I/NAV values */
1916 _E5b_HS = 0.0;
1917 _E1B_HS = 0.0;
1918 _E1B_DataInvalid = false;
1919 _E5b_DataInvalid = false;
1920 }
1921 if (bitExtracted(unsigned(datasource), 1, 9)) {
1922 _fnav = false;
1923 _inav = true;
1924 _type = t_eph::INAV;
1925 /* set unused F/NAV values */
1926 _E5a_HS = 0.0;
1927 _E5a_DataInvalid = false;
1928 }
1929 // GAL week # in RINEX is aligned/identical to continuous GPS week # used in RINEX
1930 // but GST week # started at the first GPS roll-over (continuous GPS week 1024)
1931 _TOEweek -= 1024.0;
1932 }
1933 }
1934 // =====================
1935 // BROADCAST ORBIT - 6
1936 // =====================
1937 else if (iLine == 6) {
1938 if ( readDbl(line, pos[0], fieldLen, _SISA)
1939 || readDbl(line, pos[1], fieldLen, SVhealth)
1940 || readDbl(line, pos[2], fieldLen, _BGD_1_5A)
1941 || readDbl(line, pos[3], fieldLen, _BGD_1_5B)) {
1942 _checkState = bad;
1943 return;
1944 } else {
1945 // Bit 0
1946 _E1B_DataInvalid = bitExtracted(unsigned(SVhealth), 1, 0);
1947 // Bit 1-2
1948 _E1B_HS = double(bitExtracted(unsigned(SVhealth), 2, 1));
1949 // Bit 3
1950 _E5a_DataInvalid = bitExtracted(unsigned(SVhealth), 1, 3);
1951 // Bit 4-5
1952 _E5a_HS = double(bitExtracted(unsigned(SVhealth), 2, 4));
1953 // Bit 6
1954 _E5b_DataInvalid = bitExtracted(unsigned(SVhealth), 1, 6);
1955 // Bit 7-8
1956 _E5b_HS = double(bitExtracted(unsigned(SVhealth), 2, 7));
1957 if (_fnav) {
1958 _BGD_1_5B = 0.0;
1959 }
1960 }
1961 }
1962 // =====================
1963 // BROADCAST ORBIT - 7
1964 // =====================
1965 else if (iLine == 7) {
1966 if (readDbl(line, pos[0], fieldLen, _TOT)) {
1967 _checkState = bad;
1968 return;
1969 }
1970 }
1971 }
1972 _prn.setFlag(type());
1973}
1974
1975// Compute Galileo Satellite Position (virtual)
1976////////////////////////////////////////////////////////////////////////////
1977t_irc t_ephGal::position(int GPSweek, double GPSweeks, double *xc,
1978 double *vv) const {
1979
1980 static const double omegaEarth = 7292115.1467e-11;
1981 static const double gmWGS = 398.6004418e12;
1982
1983 memset(xc, 0, 6 * sizeof(double));
1984 memset(vv, 0, 3 * sizeof(double));
1985
1986 double a0 = _sqrt_A * _sqrt_A;
1987 if (a0 == 0) {
1988 return failure;
1989 }
1990
1991 double n0 = sqrt(gmWGS / (a0 * a0 * a0));
1992
1993 bncTime tt(GPSweek, GPSweeks);
1994 double tk = tt - bncTime(_TOC.gpsw(), _TOEsec);
1995
1996 double n = n0 + _Delta_n;
1997 double M = _M0 + n * tk;
1998 double E = M;
1999 double E_last;
2000 int nLoop = 0;
2001 do {
2002 E_last = E;
2003 E = M + _e * sin(E);
2004
2005 if (++nLoop == 100) {
2006 return failure;
2007 }
2008 } while (fabs(E - E_last) * a0 > 0.001);
2009 double v = 2.0 * atan(sqrt((1.0 + _e) / (1.0 - _e)) * tan(E / 2));
2010 double u0 = v + _omega;
2011 double sin2u0 = sin(2 * u0);
2012 double cos2u0 = cos(2 * u0);
2013 double r = a0 * (1 - _e * cos(E)) + _Crc * cos2u0 + _Crs * sin2u0;
2014 double i = _i0 + _IDOT * tk + _Cic * cos2u0 + _Cis * sin2u0;
2015 double u = u0 + _Cuc * cos2u0 + _Cus * sin2u0;
2016 double xp = r * cos(u);
2017 double yp = r * sin(u);
2018 double OM = _OMEGA0 + (_OMEGADOT - omegaEarth) * tk - omegaEarth * _TOEsec;
2019
2020 double sinom = sin(OM);
2021 double cosom = cos(OM);
2022 double sini = sin(i);
2023 double cosi = cos(i);
2024 xc[0] = xp * cosom - yp * cosi * sinom;
2025 xc[1] = xp * sinom + yp * cosi * cosom;
2026 xc[2] = yp * sini;
2027
2028 double tc = tt - _TOC;
2029 xc[3] = _clock_bias + _clock_drift * tc + _clock_driftrate * tc * tc;
2030
2031 // Velocity
2032 // --------
2033 double tanv2 = tan(v / 2);
2034 double dEdM = 1 / (1 - _e * cos(E));
2035 double dotv = sqrt((1.0 + _e) / (1.0 - _e)) / cos(E / 2) / cos(E / 2)
2036 / (1 + tanv2 * tanv2) * dEdM * n;
2037 double dotu = dotv + (-_Cuc * sin2u0 + _Cus * cos2u0) * 2 * dotv;
2038 double dotom = _OMEGADOT - omegaEarth;
2039 double doti = _IDOT + (-_Cic * sin2u0 + _Cis * cos2u0) * 2 * dotv;
2040 double dotr = a0 * _e * sin(E) * dEdM * n
2041 + (-_Crc * sin2u0 + _Crs * cos2u0) * 2 * dotv;
2042 double dotx = dotr * cos(u) - r * sin(u) * dotu;
2043 double doty = dotr * sin(u) + r * cos(u) * dotu;
2044
2045 vv[0] = cosom * dotx - cosi * sinom * doty // dX / dr
2046 - xp * sinom * dotom - yp * cosi * cosom * dotom // dX / dOMEGA
2047 + yp * sini * sinom * doti; // dX / di
2048
2049 vv[1] = sinom * dotx + cosi * cosom * doty + xp * cosom * dotom
2050 - yp * cosi * sinom * dotom - yp * sini * cosom * doti;
2051
2052 vv[2] = sini * doty + yp * cosi * doti;
2053
2054 // Relativistic Correction
2055 // -----------------------
2056 xc[3] -= 4.442807309e-10 * _e * sqrt(a0) * sin(E);
2057
2058 xc[4] = _clock_drift + _clock_driftrate * tc;
2059 xc[5] = _clock_driftrate;
2060
2061 return success;
2062}
2063
2064// Health status of Galileo Ephemeris (virtual)
2065////////////////////////////////////////////////////////////////////////////
2066unsigned int t_ephGal::isUnhealthy() const {
2067 // SHS; 1 = Out of Service, 3 = In Test, 0 = Signal Ok, 2 = Extended Operations Mode
2068 if (_E5a_HS == 1 || _E5a_HS == 3 ||
2069 _E5b_HS == 1 || _E5b_HS == 3 ||
2070 _E1B_HS == 1 || _E1B_HS == 3) {
2071 return 1;
2072 }
2073 if (_E5a_DataInvalid ||
2074 _E5b_DataInvalid ||
2075 _E1B_DataInvalid) {
2076 return 1;
2077 }
2078 if (_SISA == 255.0) { // NAPA: No Accuracy Prediction Available
2079 return 1;
2080 }
2081 /*
2082 * SDD v1.3: SHS=2 leads to a newly-defined "EOM" status.
2083 * It also means that the satellite signal may be used for PNT.
2084 if (_E5aHS == 2 ||
2085 _E5bHS == 2 ||
2086 _E1_bHS == 2 ) {
2087 return 1;
2088 }
2089 */
2090 return 0;
2091
2092}
2093
2094// RINEX Format String
2095//////////////////////////////////////////////////////////////////////////////
2096QString t_ephGal::toString(double version) const {
2097
2098 QString ephStr = typeStr(_type, _prn, version);
2099 QString rnxStr = ephStr + rinexDateStr(_TOC, _prn, version);
2100
2101 QTextStream out(&rnxStr);
2102
2103 out
2104 << QString("%1%2%3\n").arg(_clock_bias, 19, 'e', 12).arg(_clock_drift, 19,
2105 'e', 12).arg(_clock_driftrate, 19, 'e', 12);
2106
2107 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
2108 // =====================
2109 // BROADCAST ORBIT - 1
2110 // =====================
2111 out
2112 << QString(fmt)
2113 .arg(_IODnav, 19, 'e', 12)
2114 .arg(_Crs, 19, 'e', 12)
2115 .arg(_Delta_n, 19, 'e', 12)
2116 .arg(_M0, 19, 'e', 12);
2117 // =====================
2118 // BROADCAST ORBIT - 2
2119 // =====================
2120 out
2121 << QString(fmt)
2122 .arg(_Cuc, 19, 'e', 12).
2123 arg(_e, 19, 'e', 12)
2124 .arg(_Cus, 19, 'e', 12)
2125 .arg(_sqrt_A, 19, 'e', 12);
2126 // =====================
2127 // BROADCAST ORBIT - 3
2128 // =====================
2129 out
2130 << QString(fmt).
2131 arg(_TOEsec, 19, 'e', 12)
2132 .arg(_Cic, 19, 'e', 12)
2133 .arg(_OMEGA0, 19, 'e', 12)
2134 .arg(_Cis, 19, 'e', 12);
2135 // =====================
2136 // BROADCAST ORBIT - 4
2137 // =====================
2138 out
2139 << QString(fmt)
2140 .arg(_i0, 19, 'e', 12)
2141 .arg(_Crc, 19, 'e', 12)
2142 .arg(_omega, 19, 'e', 12)
2143 .arg(_OMEGADOT, 19, 'e', 12);
2144 // =====================
2145 // BROADCAST ORBIT - 5/6
2146 // =====================
2147 int dataSource = 0;
2148 int SVhealth = 0;
2149 double BGD_1_5A = _BGD_1_5A;
2150 double BGD_1_5B = _BGD_1_5B;
2151 if (_fnav) {
2152 dataSource |= (1 << 1);
2153 dataSource |= (1 << 8);
2154 BGD_1_5B = 0.0;
2155 // SVhealth
2156 // Bit 3 : E5a DVS
2157 if (_E5a_DataInvalid) {
2158 SVhealth |= (1 << 3);
2159 }
2160 // Bit 4-5: E5a HS
2161 if (_E5a_HS == 1.0) {
2162 SVhealth |= (1 << 4);
2163 }
2164 else if (_E5a_HS == 2.0) {
2165 SVhealth |= (1 << 5);
2166 }
2167 else if (_E5a_HS == 3.0) {
2168 SVhealth |= (1 << 4);
2169 SVhealth |= (1 << 5);
2170 }
2171 } else if (_inav) {
2172 // Bit 2 and 0 are set because from MT1046 the data source cannot be determined
2173 // and RNXv3.03 says both can be set if the navigation messages were merged
2174 dataSource |= (1 << 0);
2175 dataSource |= (1 << 2);
2176 dataSource |= (1 << 9);
2177 // SVhealth
2178 // Bit 0 : E1-B DVS
2179 if (_E1B_DataInvalid) {
2180 SVhealth |= (1 << 0);
2181 }
2182 // Bit 1-2: E1-B HS
2183 if (_E1B_HS == 1.0) {
2184 SVhealth |= (1 << 1);
2185 }
2186 else if (_E1B_HS == 2.0) {
2187 SVhealth |= (1 << 2);
2188 }
2189 else if (_E1B_HS == 3.0) {
2190 SVhealth |= (1 << 1);
2191 SVhealth |= (1 << 2);
2192 }
2193 // Bit 6 : E5b DVS
2194 if (_E5b_DataInvalid) {
2195 SVhealth |= (1 << 6);
2196 }
2197 // Bit 7-8: E5b HS
2198 if (_E5b_HS == 1.0) {
2199 SVhealth |= (1 << 7);
2200 }
2201 else if (_E5b_HS == 2.0) {
2202 SVhealth |= (1 << 8);
2203 }
2204 else if (_E5b_HS == 3.0) {
2205 SVhealth |= (1 << 7);
2206 SVhealth |= (1 << 8);
2207 }
2208 }
2209 // =====================
2210 // BROADCAST ORBIT - 5
2211 // =====================
2212 out
2213 << QString(fmt)
2214 .arg(_IDOT, 19, 'e', 12)
2215 .arg(double(dataSource), 19, 'e', 12)
2216 .arg(_TOEweek + 1024.0, 19, 'e', 12)
2217 .arg(0.0, 19, 'e', 12);
2218 // =====================
2219 // BROADCAST ORBIT - 6
2220 // =====================
2221 out
2222 << QString(fmt)
2223 .arg(_SISA, 19, 'e', 12)
2224 .arg(double(SVhealth), 19, 'e', 12)
2225 .arg(BGD_1_5A, 19, 'e', 12)
2226 .arg(BGD_1_5B, 19, 'e', 12);
2227 // =====================
2228 // BROADCAST ORBIT - 7
2229 // =====================
2230 double tot = _TOT;
2231 if (tot == 0.9999e9 && version < 3.0) {
2232 tot = 0.0;
2233 }
2234 out
2235 << QString(fmt)
2236 .arg(tot, 19, 'e', 12)
2237 .arg("", 19, QChar(' '))
2238 .arg("", 19, QChar(' '))
2239 .arg("", 19, QChar(' '));
2240
2241 return rnxStr;
2242}
2243
2244// Constructor
2245//////////////////////////////////////////////////////////////////////////////
2246t_ephSBAS::t_ephSBAS(double rnxVersion, const QStringList &lines, const QString typeStr) {
2247
2248 setType(typeStr);
2249
2250 const int nLines = 4;
2251
2252 // Source RINEX version < 4
2253 if (type() == t_eph::undefined) {
2254 _type = t_eph::SBASL1;
2255 }
2256
2257 if (lines.size() != nLines) {
2258 _checkState = bad;
2259 return;
2260 }
2261
2262 // RINEX Format
2263 // ------------
2264 int fieldLen = 19;
2265
2266 int pos[4];
2267 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
2268 pos[1] = pos[0] + fieldLen;
2269 pos[2] = pos[1] + fieldLen;
2270 pos[3] = pos[2] + fieldLen;
2271
2272 // Read four lines
2273 // ---------------
2274 for (int iLine = 0; iLine < nLines; iLine++) {
2275 QString line = lines[iLine];
2276
2277 if (iLine == 0) {
2278 QTextStream in(line.left(pos[1]).toLatin1());
2279
2280 int year, month, day, hour, min;
2281 double sec;
2282
2283 QString prnStr, n;
2284 in >> prnStr;
2285 if (prnStr.size() == 1 && prnStr[0] == 'S') {
2286 in >> n;
2287 prnStr.append(n);
2288 }
2289 if (prnStr.at(0) == 'S') {
2290 _prn.set('S', prnStr.mid(1).toInt());
2291 } else {
2292 _prn.set('S', prnStr.toInt());
2293 }
2294 _prn.setFlag(type());
2295
2296 in >> year >> month >> day >> hour >> min >> sec;
2297
2298 if (year < 80) {
2299 year += 2000;
2300 } else if (year < 100) {
2301 year += 1900;
2302 }
2303
2304 _TOC.set(year, month, day, hour, min, sec);
2305
2306 if ( readDbl(line, pos[1], fieldLen, _agf0)
2307 || readDbl(line, pos[2], fieldLen, _agf1)
2308 || readDbl(line, pos[3], fieldLen, _TOT)) {
2309 _checkState = bad;
2310 return;
2311 }
2312 }
2313 // =====================
2314 // BROADCAST ORBIT - 1
2315 // =====================
2316 else if (iLine == 1) {
2317 if ( readDbl(line, pos[0], fieldLen, _x_pos)
2318 || readDbl(line, pos[1], fieldLen, _x_vel)
2319 || readDbl(line, pos[2], fieldLen, _x_acc)
2320 || readDbl(line, pos[3], fieldLen, _health)) {
2321 _checkState = bad;
2322 return;
2323 }
2324 }
2325 // =====================
2326 // BROADCAST ORBIT - 2
2327 // =====================
2328 else if (iLine == 2) {
2329 if ( readDbl(line, pos[0], fieldLen, _y_pos)
2330 || readDbl(line, pos[1], fieldLen, _y_vel)
2331 || readDbl(line, pos[2], fieldLen, _y_acc)
2332 || readDbl(line, pos[3], fieldLen, _ura)) {
2333 _checkState = bad;
2334 return;
2335 }
2336 }
2337 // =====================
2338 // BROADCAST ORBIT - 3
2339 // =====================
2340 else if (iLine == 3) {
2341 double iodn;
2342 if ( readDbl(line, pos[0], fieldLen, _z_pos)
2343 || readDbl(line, pos[1], fieldLen, _z_vel)
2344 || readDbl(line, pos[2], fieldLen, _z_acc)
2345 || readDbl(line, pos[3], fieldLen, iodn)) {
2346 _checkState = bad;
2347 return;
2348 } else {
2349 _IODN = int(iodn);
2350 }
2351 }
2352 }
2353 _x_pos *= 1.e3;
2354 _y_pos *= 1.e3;
2355 _z_pos *= 1.e3;
2356 _x_vel *= 1.e3;
2357 _y_vel *= 1.e3;
2358 _z_vel *= 1.e3;
2359 _x_acc *= 1.e3;
2360 _y_acc *= 1.e3;
2361 _z_acc *= 1.e3;
2362}
2363
2364// IOD of SBAS Ephemeris (virtual)
2365////////////////////////////////////////////////////////////////////////////
2366
2367unsigned int t_ephSBAS::IOD() const {
2368 unsigned char buffer[80];
2369 int size = 0;
2370 int numbits = 0;
2371 long long bitbuffer = 0;
2372 unsigned char *startbuffer = buffer;
2373
2374 SBASADDBITSFLOAT(30, this->_x_pos, 0.08)
2375 SBASADDBITSFLOAT(30, this->_y_pos, 0.08)
2376 SBASADDBITSFLOAT(25, this->_z_pos, 0.4)
2377 SBASADDBITSFLOAT(17, this->_x_vel, 0.000625)
2378 SBASADDBITSFLOAT(17, this->_y_vel, 0.000625)
2379 SBASADDBITSFLOAT(18, this->_z_vel, 0.004)
2380 SBASADDBITSFLOAT(10, this->_x_acc, 0.0000125)
2381 SBASADDBITSFLOAT(10, this->_y_acc, 0.0000125)
2382 SBASADDBITSFLOAT(10, this->_z_acc, 0.0000625)
2383 SBASADDBITSFLOAT(12, this->_agf0,
2384 1.0 / static_cast<double>(1 << 30) / static_cast<double>(1 << 1))
2385 SBASADDBITSFLOAT(8, this->_agf1,
2386 1.0 / static_cast<double>(1 << 30) / static_cast<double>(1 << 10))
2387 SBASADDBITS(5, 0); // the last byte is filled by 0-bits to obtain a length of an integer multiple of 8
2388
2389 return CRC24(size, startbuffer);
2390}
2391
2392// Compute SBAS Satellite Position (virtual)
2393////////////////////////////////////////////////////////////////////////////
2394t_irc t_ephSBAS::position(int GPSweek, double GPSweeks, double *xc,
2395 double *vv) const {
2396
2397 bncTime tt(GPSweek, GPSweeks);
2398 double dt = tt - _TOC;
2399
2400 xc[0] = _x_pos + _x_vel * dt + _x_acc * dt * dt / 2.0;
2401 xc[1] = _y_pos + _y_vel * dt + _y_acc * dt * dt / 2.0;
2402 xc[2] = _z_pos + _z_vel * dt + _z_acc * dt * dt / 2.0;
2403
2404 vv[0] = _x_vel + _x_acc * dt;
2405 vv[1] = _y_vel + _y_acc * dt;
2406 vv[2] = _z_vel + _z_acc * dt;
2407
2408 xc[3] = _agf0 + _agf1 * dt;
2409
2410 xc[4] = _agf1;
2411 xc[5] = 0.0;
2412
2413 return success;
2414}
2415
2416// Health status of SBAS Ephemeris (virtual)
2417////////////////////////////////////////////////////////////////////////////
2418unsigned int t_ephSBAS::isUnhealthy() const {
2419
2420// Bit 5
2421 bool URAindexIs15 = bitExtracted(unsigned(_health), 1, 5);
2422 if (URAindexIs15) {
2423 // in this case it is recommended
2424 // to set the bits 0,1,2,3 to 1 (MT17health = 15)
2425 return 1;
2426 }
2427
2428 // Bit 4
2429 bool MT17HealthIsUnavailable = bitExtracted(unsigned(_health), 1, 4);
2430 if (MT17HealthIsUnavailable) {
2431 return 0;
2432 }
2433
2434 // Bit 0-3
2435 int MT17health = bitExtracted(unsigned(_health), 4, 0);
2436 if (MT17health) {
2437 return 1;
2438 }
2439
2440 return 0;
2441}
2442
2443// RINEX Format String
2444//////////////////////////////////////////////////////////////////////////////
2445QString t_ephSBAS::toString(double version) const {
2446
2447 QString ephStr = typeStr(_type, _prn, version);
2448 QString rnxStr = ephStr + rinexDateStr(_TOC, _prn, version);
2449
2450 QTextStream out(&rnxStr);
2451
2452 out
2453 << QString("%1%2%3\n")
2454 .arg(_agf0, 19, 'e', 12)
2455 .arg(_agf1, 19, 'e', 12)
2456 .arg(_TOT, 19, 'e', 12);
2457
2458 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
2459 // =====================
2460 // BROADCAST ORBIT - 1
2461 // =====================
2462 out
2463 << QString(fmt)
2464 .arg(1.e-3 * _x_pos, 19, 'e', 12)
2465 .arg(1.e-3 * _x_vel, 19, 'e', 12)
2466 .arg(1.e-3 * _x_acc, 19, 'e', 12)
2467 .arg(_health, 19, 'e', 12);
2468 // =====================
2469 // BROADCAST ORBIT - 2
2470 // =====================
2471 out
2472 << QString(fmt)
2473 .arg(1.e-3 * _y_pos, 19, 'e', 12)
2474 .arg(1.e-3 * _y_vel, 19, 'e', 12)
2475 .arg(1.e-3 * _y_acc, 19, 'e', 12)
2476 .arg(_ura, 19, 'e', 12);
2477 // =====================
2478 // BROADCAST ORBIT - 3
2479 // =====================
2480 out
2481 << QString(fmt)
2482 .arg(1.e-3 * _z_pos, 19, 'e', 12)
2483 .arg(1.e-3 * _z_vel, 19, 'e', 12)
2484 .arg(1.e-3 * _z_acc, 19, 'e', 12)
2485 .arg(double(_IODN), 19, 'e', 12);
2486
2487 return rnxStr;
2488}
2489
2490// Constructor
2491//////////////////////////////////////////////////////////////////////////////
2492t_ephBDS::t_ephBDS(double rnxVersion, const QStringList &lines, const QString typeStr) {
2493
2494 setType(typeStr);
2495
2496 int nLines = 8;
2497
2498 if (type() == t_eph::CNV1 ||
2499 type() == t_eph::CNV2) {
2500 nLines += 2;
2501 }
2502 if (type() == t_eph::CNV3) {
2503 nLines += 1;
2504 }
2505
2506 if (lines.size() != nLines) {
2507 _checkState = bad;
2508 return;
2509 }
2510
2511 // RINEX Format
2512 // ------------
2513 int fieldLen = 19;
2514
2515 int pos[4];
2516 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
2517 pos[1] = pos[0] + fieldLen;
2518 pos[2] = pos[1] + fieldLen;
2519 pos[3] = pos[2] + fieldLen;
2520
2521 // Read eight lines
2522 // ----------------
2523 for (int iLine = 0; iLine < nLines; iLine++) {
2524 QString line = lines[iLine];
2525
2526 if (iLine == 0) {
2527 QTextStream in(line.left(pos[1]).toLatin1());
2528
2529 int year, month, day, hour, min;
2530 double sec;
2531
2532 QString prnStr, n;
2533 in >> prnStr;
2534 if (prnStr.size() == 1 && prnStr[0] == 'C') {
2535 in >> n;
2536 prnStr.append(n);
2537 }
2538 if (prnStr.at(0) == 'C') {
2539 _prn.set('C', prnStr.mid(1).toInt());
2540 } else {
2541 _prn.set('C', prnStr.toInt());
2542 }
2543
2544 in >> year >> month >> day >> hour >> min >> sec;
2545 if (year < 80) {
2546 year += 2000;
2547 } else if (year < 100) {
2548 year += 1900;
2549 }
2550
2551 _TOC.setBDS(year, month, day, hour, min, sec);
2552
2553 if ( readDbl(line, pos[1], fieldLen, _clock_bias)
2554 || readDbl(line, pos[2], fieldLen, _clock_drift)
2555 || readDbl(line, pos[3], fieldLen, _clock_driftrate)) {
2556 _checkState = bad;
2557 return;
2558 }
2559 }
2560 // =====================
2561 // BROADCAST ORBIT - 1
2562 // =====================
2563 else if (iLine == 1) {
2564 if (type() == t_eph::D1 ||
2565 type() == t_eph::D2 ||
2566 type() == t_eph::undefined) {
2567 double aode;
2568 if ( readDbl(line, pos[0], fieldLen, aode)
2569 || readDbl(line, pos[1], fieldLen, _Crs)
2570 || readDbl(line, pos[2], fieldLen, _Delta_n)
2571 || readDbl(line, pos[3], fieldLen, _M0)) {
2572 _checkState = bad;
2573 return;
2574 }
2575 _AODE = int(aode);
2576 }
2577 else { //CNV1, CNV2, CNV3
2578 if ( readDbl(line, pos[0], fieldLen, _ADOT)
2579 || readDbl(line, pos[1], fieldLen, _Crs)
2580 || readDbl(line, pos[2], fieldLen, _Delta_n)
2581 || readDbl(line, pos[3], fieldLen, _M0)) {
2582 _checkState = bad;
2583 return;
2584 }
2585 }
2586 }
2587 // =====================
2588 // BROADCAST ORBIT - 2
2589 // =====================
2590 else if (iLine == 2) {
2591 if ( readDbl(line, pos[0], fieldLen, _Cuc)
2592 || readDbl(line, pos[1], fieldLen, _e)
2593 || readDbl(line, pos[2], fieldLen, _Cus)
2594 || readDbl(line, pos[3], fieldLen, _sqrt_A)) {
2595 _checkState = bad;
2596 return;
2597 }
2598 }
2599 // =====================
2600 // BROADCAST ORBIT - 3
2601 // =====================
2602 else if (iLine == 3) {
2603 if ( readDbl(line, pos[0], fieldLen, _TOEsec)
2604 || readDbl(line, pos[1], fieldLen, _Cic)
2605 || readDbl(line, pos[2], fieldLen, _OMEGA0)
2606 || readDbl(line, pos[3], fieldLen, _Cis)) {
2607 _checkState = bad;
2608 return;
2609 }
2610 }
2611 // =====================
2612 // BROADCAST ORBIT - 4
2613 // =====================
2614 else if (iLine == 4) {
2615 if ( readDbl(line, pos[0], fieldLen, _i0)
2616 || readDbl(line, pos[1], fieldLen, _Crc)
2617 || readDbl(line, pos[2], fieldLen, _omega)
2618 || readDbl(line, pos[3], fieldLen, _OMEGADOT)) {
2619 _checkState = bad;
2620 return;
2621 }
2622 else {
2623 // Source RINEX version < 4
2624 if (type() == t_eph::undefined) {
2625 const double iMaxGEO = 10.0 / 180.0 * M_PI;
2626 if (_i0 > iMaxGEO) {
2627 _type = t_eph::D1;
2628 }
2629 else {
2630 _type = t_eph::D2;
2631 }
2632 }
2633 }
2634 }
2635 // =====================
2636 // BROADCAST ORBIT - 5
2637 // =====================
2638 else if (iLine == 5) {
2639 if (type() == t_eph::CNV1 ||
2640 type() == t_eph::CNV2 ||
2641 type() == t_eph::CNV3) {
2642 if ( readDbl(line, pos[0], fieldLen, _IDOT)
2643 || readDbl(line, pos[1], fieldLen, _Delta_n_dot)
2644 || readDbl(line, pos[2], fieldLen, _satType)
2645 || readDbl(line, pos[3], fieldLen, _top)) {
2646 _checkState = bad;
2647 return;
2648 }
2649 }
2650 else { // D1, D2
2651 if ( readDbl(line, pos[0], fieldLen, _IDOT)
2652 || readDbl(line, pos[2], fieldLen, _BDTweek)) {
2653 _checkState = bad;
2654 return;
2655 }
2656 }
2657 }
2658 // =====================
2659 // BROADCAST ORBIT - 6
2660 // =====================
2661 else if (iLine == 6) {
2662 if (type() == t_eph::CNV1 ||
2663 type() == t_eph::CNV2 ||
2664 type() == t_eph::CNV3) {
2665 if ( readDbl(line, pos[0], fieldLen, _SISAI_oe)
2666 || readDbl(line, pos[1], fieldLen, _SISAI_ocb)
2667 || readDbl(line, pos[2], fieldLen, _SISAI_oc1)
2668 || readDbl(line, pos[3], fieldLen, _SISAI_oc2)) {
2669 _checkState = bad;
2670 return;
2671 }
2672 }
2673 else { // D1, D2
2674 double SatH1;
2675 if ( readDbl(line, pos[0], fieldLen, _ura)
2676 || readDbl(line, pos[1], fieldLen, SatH1)
2677 || readDbl(line, pos[2], fieldLen, _TGD1)
2678 || readDbl(line, pos[3], fieldLen, _TGD2)) {
2679 _checkState = bad;
2680 return;
2681 }
2682 _SatH1 = int(SatH1);
2683 }
2684 }
2685 // =====================
2686 // BROADCAST ORBIT - 7
2687 // =====================
2688 else if (iLine == 7) {
2689 if (type() == t_eph::CNV1) {
2690 if ( readDbl(line, pos[0], fieldLen, _ISC_B1Cd)
2691 || readDbl(line, pos[2], fieldLen, _TGD_B1Cp)
2692 || readDbl(line, pos[3], fieldLen, _TGD_B2ap)) {
2693 _checkState = bad;
2694 return;
2695 }
2696 }
2697 else if (type() == t_eph::CNV2) {
2698 if ( readDbl(line, pos[1], fieldLen, _ISC_B2ad)
2699 || readDbl(line, pos[2], fieldLen, _TGD_B1Cp)
2700 || readDbl(line, pos[3], fieldLen, _TGD_B2ap)) {
2701 _checkState = bad;
2702 return;
2703 }
2704 }
2705 else if (type() == t_eph::CNV3) {
2706 double health;
2707 if ( readDbl(line, pos[0], fieldLen, _SISMAI)
2708 || readDbl(line, pos[1], fieldLen, health)
2709 || readDbl(line, pos[2], fieldLen, _INTEGRITYF_B2b)
2710 || readDbl(line, pos[3], fieldLen, _TGD_B2bI)) {
2711 _checkState = bad;
2712 return;
2713 }
2714 _health = int(health);
2715 }
2716 else { // D1, D2
2717 double aodc;
2718 if ( readDbl(line, pos[0], fieldLen, _TOT)
2719 || readDbl(line, pos[1], fieldLen, aodc)) {
2720 _checkState = bad;
2721 return;
2722 }
2723 if (_TOT == 0.9999e9) { // 0.9999e9 means not known (RINEX standard)
2724 _TOT = _TOEsec;
2725 }
2726 _AODC = int(aodc);
2727 }
2728 }
2729 // =====================
2730 // BROADCAST ORBIT - 8
2731 // =====================
2732 else if (iLine == 8) {
2733 double health;
2734 if (type() == t_eph::CNV1) {
2735 if ( readDbl(line, pos[0], fieldLen, _SISMAI)
2736 || readDbl(line, pos[1], fieldLen, health)
2737 || readDbl(line, pos[2], fieldLen, _INTEGRITYF_B1C)
2738 || readDbl(line, pos[3], fieldLen, _IODC)) {
2739 _checkState = bad;
2740 return;
2741 }
2742 _health = int(health);
2743 }
2744 else if (type() == t_eph::CNV2) {
2745 if ( readDbl(line, pos[0], fieldLen, _SISMAI)
2746 || readDbl(line, pos[1], fieldLen, health)
2747 || readDbl(line, pos[2], fieldLen, _INTEGRITYF_B2aB1C)
2748 || readDbl(line, pos[3], fieldLen, _IODC)) {
2749 _checkState = bad;
2750 return;
2751 }
2752 _health = int(health);
2753 }
2754 else if (type() == t_eph::CNV3) {
2755 if (readDbl(line, pos[0], fieldLen, _TOT)) {
2756 _checkState = bad;
2757 return;
2758 }
2759 }
2760 }
2761 // =====================
2762 // BROADCAST ORBIT - 9
2763 // =====================
2764 else if (iLine == 9) {
2765 if (type() == t_eph::CNV1 ||
2766 type() == t_eph::CNV2) {
2767 if ( readDbl(line, pos[0], fieldLen, _TOT)
2768 || readDbl(line, pos[3], fieldLen, _IODE)) {
2769 _checkState = bad;
2770 return;
2771 }
2772 }
2773 }
2774 }
2775 _prn.setFlag(type());
2776
2777 _TOE.setBDS(int(_BDTweek), _TOEsec);
2778 // remark: actually should be computed from second_tot
2779 // but it seems to be unreliable in RINEX files
2780 //_TOT = _TOC.bdssec();
2781}
2782
2783// IOD of BDS Ephemeris (virtual)
2784////////////////////////////////////////////////////////////////////////////
2785unsigned int t_ephBDS::IOD() const {
2786 if (type() == t_eph::D1 ||
2787 type() == t_eph::D2) {
2788 return (int(_TOC.gpssec()) / 720) % 240;
2789 }
2790 else {
2791 return _IODE;
2792 }
2793}
2794
2795// Compute BDS Satellite Position (virtual)
2796//////////////////////////////////////////////////////////////////////////////
2797t_irc t_ephBDS::position(int GPSweek, double GPSweeks, double *xc,
2798 double *vv) const {
2799
2800 static const double gmBDS = 398.6004418e12;
2801 static const double omegaBDS = 7292115.0000e-11;
2802
2803 xc[0] = xc[1] = xc[2] = xc[3] = 0.0;
2804 vv[0] = vv[1] = vv[2] = 0.0;
2805
2806 bncTime tt(GPSweek, GPSweeks);
2807
2808 if (_sqrt_A == 0) {
2809 return failure;
2810 }
2811 double a0 = _sqrt_A * _sqrt_A;
2812
2813 double n0 = sqrt(gmBDS / (a0 * a0 * a0));
2814 double tk = tt - _TOE;
2815 double n = n0 + _Delta_n;
2816 double M = _M0 + n * tk;
2817 double E = M;
2818 double E_last;
2819 int nLoop = 0;
2820 do {
2821 E_last = E;
2822 E = M + _e * sin(E);
2823
2824 if (++nLoop == 100) {
2825 return failure;
2826 }
2827 } while (fabs(E - E_last) * a0 > 0.001);
2828
2829 double v = atan2(sqrt(1 - _e * _e) * sin(E), cos(E) - _e);
2830 double u0 = v + _omega;
2831 double sin2u0 = sin(2 * u0);
2832 double cos2u0 = cos(2 * u0);
2833 double r = a0 * (1 - _e * cos(E)) + _Crc * cos2u0 + _Crs * sin2u0;
2834 double i = _i0 + _IDOT * tk + _Cic * cos2u0 + _Cis * sin2u0;
2835 double u = u0 + _Cuc * cos2u0 + _Cus * sin2u0;
2836 double xp = r * cos(u);
2837 double yp = r * sin(u);
2838 double toesec = (_TOE.gpssec() - 14.0);
2839 double sinom = 0;
2840 double cosom = 0;
2841 double sini = 0;
2842 double cosi = 0;
2843
2844 // Velocity
2845 // --------
2846 double tanv2 = tan(v / 2);
2847 double dEdM = 1 / (1 - _e * cos(E));
2848 double dotv = sqrt((1.0 + _e) / (1.0 - _e)) / cos(E / 2) / cos(E / 2)
2849 / (1 + tanv2 * tanv2) * dEdM * n;
2850 double dotu = dotv + (-_Cuc * sin2u0 + _Cus * cos2u0) * 2 * dotv;
2851 double doti = _IDOT + (-_Cic * sin2u0 + _Cis * cos2u0) * 2 * dotv;
2852 double dotr = a0 * _e * sin(E) * dEdM * n
2853 + (-_Crc * sin2u0 + _Crs * cos2u0) * 2 * dotv;
2854
2855 double dotx = dotr * cos(u) - r * sin(u) * dotu;
2856 double doty = dotr * sin(u) + r * cos(u) * dotu;
2857
2858 const double iMaxGEO = 10.0 / 180.0 * M_PI;
2859
2860 // MEO/IGSO satellite
2861 // ------------------
2862 if (_i0 > iMaxGEO) {
2863 double OM = _OMEGA0 + (_OMEGADOT - omegaBDS) * tk - omegaBDS * toesec;
2864
2865 sinom = sin(OM);
2866 cosom = cos(OM);
2867 sini = sin(i);
2868 cosi = cos(i);
2869
2870 xc[0] = xp * cosom - yp * cosi * sinom;
2871 xc[1] = xp * sinom + yp * cosi * cosom;
2872 xc[2] = yp * sini;
2873
2874 // Velocity
2875 // --------
2876
2877 double dotom = _OMEGADOT - t_CST::omega;
2878
2879 vv[0] = cosom * dotx - cosi * sinom * doty // dX / dr
2880 - xp * sinom * dotom - yp * cosi * cosom * dotom // dX / dOMEGA
2881 + yp * sini * sinom * doti; // dX / di
2882
2883 vv[1] = sinom * dotx + cosi * cosom * doty + xp * cosom * dotom
2884 - yp * cosi * sinom * dotom - yp * sini * cosom * doti;
2885
2886 vv[2] = sini * doty + yp * cosi * doti;
2887
2888 }
2889
2890 // GEO satellite
2891 // -------------
2892 else {
2893 double OM = _OMEGA0 + _OMEGADOT * tk - omegaBDS * toesec;
2894 double ll = omegaBDS * tk;
2895
2896 sinom = sin(OM);
2897 cosom = cos(OM);
2898 sini = sin(i);
2899 cosi = cos(i);
2900
2901 double xx = xp * cosom - yp * cosi * sinom;
2902 double yy = xp * sinom + yp * cosi * cosom;
2903 double zz = yp * sini;
2904
2905 Matrix RX = BNC_PPP::t_astro::rotX(-5.0 / 180.0 * M_PI);
2906 Matrix RZ = BNC_PPP::t_astro::rotZ(ll);
2907
2908 ColumnVector X1(3);
2909 X1 << xx << yy << zz;
2910 ColumnVector X2 = RZ * RX * X1;
2911
2912 xc[0] = X2(1);
2913 xc[1] = X2(2);
2914 xc[2] = X2(3);
2915
2916 double dotom = _OMEGADOT;
2917
2918 double vx = cosom * dotx - cosi * sinom * doty - xp * sinom * dotom
2919 - yp * cosi * cosom * dotom + yp * sini * sinom * doti;
2920
2921 double vy = sinom * dotx + cosi * cosom * doty + xp * cosom * dotom
2922 - yp * cosi * sinom * dotom - yp * sini * cosom * doti;
2923
2924 double vz = sini * doty + yp * cosi * doti;
2925
2926 ColumnVector V(3);
2927 V << vx << vy << vz;
2928
2929 Matrix RdotZ(3, 3);
2930 double C = cos(ll);
2931 double S = sin(ll);
2932 Matrix UU(3, 3);
2933 UU[0][0] = -S;
2934 UU[0][1] = +C;
2935 UU[0][2] = 0.0;
2936 UU[1][0] = -C;
2937 UU[1][1] = -S;
2938 UU[1][2] = 0.0;
2939 UU[2][0] = 0.0;
2940 UU[2][1] = 0.0;
2941 UU[2][2] = 0.0;
2942 RdotZ = omegaBDS * UU;
2943
2944 ColumnVector VV(3);
2945 VV = RZ * RX * V + RdotZ * RX * X1;
2946
2947 vv[0] = VV(1);
2948 vv[1] = VV(2);
2949 vv[2] = VV(3);
2950 }
2951
2952 double tc = tt - _TOC;
2953 xc[3] = _clock_bias + _clock_drift * tc + _clock_driftrate * tc * tc;
2954
2955// dotC = _clock_drift + _clock_driftrate*tc
2956// - 4.442807309e-10*_e * sqrt(a0) * cos(E) * dEdM * n;
2957
2958 // Relativistic Correction
2959 // -----------------------
2960 xc[3] -= 4.442807309e-10 * _e * sqrt(a0) * sin(E);
2961
2962 xc[4] = _clock_drift + _clock_driftrate * tc;
2963 xc[5] = _clock_driftrate;
2964
2965 return success;
2966}
2967
2968// Health status of SBAS Ephemeris (virtual)
2969////////////////////////////////////////////////////////////////////////////
2970unsigned int t_ephBDS::isUnhealthy() const {
2971
2972 if (type() == t_eph::CNV1 ||
2973 type() == t_eph::CNV2 ||
2974 type() == t_eph::CNV3) {
2975 return static_cast<unsigned int>(_health);
2976 }
2977
2978 return static_cast<unsigned int>(_SatH1);
2979
2980}
2981
2982// RINEX Format String
2983//////////////////////////////////////////////////////////////////////////////
2984QString t_ephBDS::toString(double version) const {
2985
2986 if (version < 4.0 &&
2987 (type() == t_eph::CNV1 ||
2988 type() == t_eph::CNV2 ||
2989 type() == t_eph::CNV3 )) {
2990 return "";
2991 }
2992
2993 QString ephStr = typeStr(_type, _prn, version);
2994 QString rnxStr = ephStr + rinexDateStr(_TOC - 14.0, _prn, version);
2995
2996 QTextStream out(&rnxStr);
2997
2998 out
2999 << QString("%1%2%3\n")
3000 .arg(_clock_bias, 19, 'e', 12)
3001 .arg(_clock_drift, 19, 'e', 12)
3002 .arg(_clock_driftrate, 19, 'e', 12);
3003
3004 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
3005 // =====================
3006 // BROADCAST ORBIT - 1
3007 // =====================
3008 if (type() == t_eph::D1 ||
3009 type() == t_eph::D2 ||
3010 type() == t_eph::undefined) {
3011 out
3012 << QString(fmt)
3013 .arg(double(_AODE), 19, 'e', 12)
3014 .arg(_Crs, 19, 'e', 12)
3015 .arg(_Delta_n, 19, 'e', 12)
3016 .arg(_M0, 19, 'e', 12);
3017 }
3018 else { //CNV1, CNV2, CNV3
3019 out
3020 << QString(fmt)
3021 .arg(_ADOT, 19, 'e', 12)
3022 .arg(_Crs, 19, 'e', 12)
3023 .arg(_Delta_n, 19, 'e', 12)
3024 .arg(_M0, 19, 'e', 12);
3025 }
3026
3027 // =====================
3028 // BROADCAST ORBIT - 2
3029 // =====================
3030 out
3031 << QString(fmt)
3032 .arg(_Cuc, 19, 'e', 12)
3033 .arg(_e, 19, 'e', 12)
3034 .arg(_Cus, 19, 'e', 12)
3035 .arg(_sqrt_A, 19, 'e', 12);
3036
3037 // =====================
3038 // BROADCAST ORBIT - 3
3039 // =====================
3040 out
3041 << QString(fmt)
3042 .arg(_TOEsec, 19, 'e', 12)
3043 .arg(_Cic, 19, 'e', 12)
3044 .arg(_OMEGA0, 19, 'e', 12)
3045 .arg(_Cis, 19, 'e', 12);
3046 // =====================
3047 // BROADCAST ORBIT - 4
3048 // =====================
3049 out
3050 << QString(fmt)
3051 .arg(_i0, 19, 'e', 12)
3052 .arg(_Crc, 19, 'e', 12)
3053 .arg(_omega, 19, 'e', 12)
3054 .arg(_OMEGADOT, 19, 'e', 12);
3055 // =====================
3056 // BROADCAST ORBIT - 5
3057 // =====================
3058 if (type() == t_eph::CNV1 ||
3059 type() == t_eph::CNV2 ||
3060 type() == t_eph::CNV3) {
3061 out
3062 << QString(fmt)
3063 .arg(_IDOT, 19, 'e', 12)
3064 .arg(_Delta_n_dot, 19, 'e', 12)
3065 .arg(_satType, 19, 'e', 12)
3066 .arg(_top, 19, 'e', 12);
3067 }
3068 else { // D1, D2,
3069 out
3070 << QString(fmt)
3071 .arg(_IDOT, 19, 'e', 12)
3072 .arg("", 19, QChar(' '))
3073 .arg(_BDTweek, 19, 'e', 12)
3074 .arg("", 19, QChar(' '));
3075 }
3076 // =====================
3077 // BROADCAST ORBIT - 6
3078 // =====================
3079 if (type() == t_eph::CNV1 ||
3080 type() == t_eph::CNV2 ||
3081 type() == t_eph::CNV3) {
3082 out
3083 << QString(fmt)
3084 .arg(_SISAI_oe, 19, 'e', 12)
3085 .arg(_SISAI_ocb, 19, 'e', 12)
3086 .arg(_SISAI_oc1, 19, 'e', 12)
3087 .arg(_SISAI_oc2, 19, 'e', 12);
3088 }
3089 else { // D1, D2, undefined
3090 out
3091 << QString(fmt)
3092 .arg(_ura, 19, 'e', 12)
3093 .arg(double(_SatH1), 19, 'e', 12)
3094 .arg(_TGD1, 19, 'e', 12)
3095 .arg(_TGD2, 19, 'e', 12);
3096 }
3097 // =====================
3098 // BROADCAST ORBIT - 7
3099 // =====================
3100 if (type() == t_eph::CNV1) {
3101 out
3102 << QString(fmt)
3103 .arg(_ISC_B1Cd, 19, 'e', 12)
3104 .arg("", 19, QChar(' '))
3105 .arg(_TGD_B1Cp, 19, 'e', 12)
3106 .arg(_TGD_B2ap, 19, 'e', 12);
3107 }
3108 else if (type() == t_eph::CNV2) {
3109 out
3110 << QString(fmt)
3111 .arg("", 19, QChar(' '))
3112 .arg(_ISC_B2ad, 19, 'e', 12)
3113 .arg(_TGD_B1Cp, 19, 'e', 12)
3114 .arg(_TGD_B2ap, 19, 'e', 12);
3115 }
3116 else if (type() == t_eph::CNV3) {
3117 out
3118 << QString(fmt)
3119 .arg(_SISMAI, 19, 'e', 12)
3120 .arg(double(_health), 19, 'e', 12)
3121 .arg(_INTEGRITYF_B2b, 19, 'e', 12)
3122 .arg(_TGD_B2bI, 19, 'e', 12);
3123 }
3124 else { // D1, D2, undefined
3125 double tots = 0.0;
3126 if (_receptDateTime.isValid()) { // RTCM stream input
3127 tots = _TOE.bdssec();
3128 } else { // RINEX input
3129 tots = _TOT;
3130 }
3131 out
3132 << QString(fmt)
3133 .arg(tots, 19, 'e', 12)
3134 .arg(double(_AODC), 19, 'e', 12)
3135 .arg("", 19, QChar(' '))
3136 .arg("", 19, QChar(' '));
3137 }
3138
3139 // =====================
3140 // BROADCAST ORBIT - 8
3141 // =====================
3142 if (type() == t_eph::CNV1) {
3143 out
3144 << QString(fmt)
3145 .arg(_SISMAI, 19, 'e', 12)
3146 .arg(double(_health), 19, 'e', 12)
3147 .arg(_INTEGRITYF_B1C, 19, 'e', 12)
3148 .arg(_IODC, 19, 'e', 12);
3149 }
3150 else if (type() == t_eph::CNV2) {
3151 out
3152 << QString(fmt)
3153 .arg(_SISMAI, 19, 'e', 12)
3154 .arg(double(_health), 19, 'e', 12)
3155 .arg(_INTEGRITYF_B2aB1C, 19, 'e', 12)
3156 .arg(_IODC, 19, 'e', 12);
3157 }
3158 else if (type() == t_eph::CNV3) {
3159 out
3160 << QString(fmt)
3161 .arg(_TOT, 19, 'e', 12)
3162 .arg("", 19, QChar(' '))
3163 .arg("", 19, QChar(' '))
3164 .arg("", 19, QChar(' '));
3165 }
3166
3167 // =====================
3168 // BROADCAST ORBIT - 9
3169 // =====================
3170 if (type() == t_eph::CNV1 ||
3171 type() == t_eph::CNV2) {
3172 out
3173 << QString(fmt)
3174 .arg(_TOT, 19, 'e', 12)
3175 .arg("", 19, QChar(' '))
3176 .arg("", 19, QChar(' '))
3177 .arg(_IODE, 19, 'e', 12);
3178 }
3179
3180 return rnxStr;
3181}
Note: See TracBrowser for help on using the repository browser.