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

Last change on this file since 10995 was 10993, checked in by stuerze, 3 weeks ago

minor changes to allow different formulas for the computation of the relativistic effects in GPS position determination for RTCM-SSR and IGS-SSR

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