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

Last change on this file since 10971 was 10954, checked in by stuerze, 3 months ago

minor changes to investigate a special GLO behavior

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