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

Last change on this file since 10542 was 10533, checked in by stuerze, 11 months ago

Service and RTCM CRS encoding and decoding as well as Helmert parameter decoding added + some re-organisation

File size: 70.9 KB
Line 
1#include <sstream>
2#include <iostream>
3#include <iomanip>
4#include <cstring>
5
6#include <newmatio.h>
7
8#include "ephemeris.h"
9#include "bncutils.h"
10#include "bnctime.h"
11#include "bnccore.h"
12#include "bncutils.h"
13#include "satObs.h"
14#include "pppInclude.h"
15#include "pppModel.h"
16#include "RTCM3/bits.h"
17
18using namespace std;
19
20// Constructor
21////////////////////////////////////////////////////////////////////////////
22t_eph::t_eph() {
23 _checkState = unchecked;
24 _navType = undefined;
25 _orbCorr = 0;
26 _clkCorr = 0;
27}
28// Destructor
29////////////////////////////////////////////////////////////////////////////
30t_eph::~t_eph() {
31 if (_orbCorr)
32 delete _orbCorr;
33 if (_clkCorr)
34 delete _clkCorr;
35}
36
37//
38////////////////////////////////////////////////////////////////////////////
39void t_eph::setOrbCorr(const t_orbCorr* orbCorr) {
40 if (_orbCorr) {
41 delete _orbCorr;
42 _orbCorr = 0;
43 }
44 _orbCorr = new t_orbCorr(*orbCorr);
45}
46
47//
48////////////////////////////////////////////////////////////////////////////
49void t_eph::setClkCorr(const t_clkCorr* clkCorr) {
50 if (_clkCorr) {
51 delete _clkCorr;
52 _clkCorr = 0;
53 }
54 _clkCorr = new t_clkCorr(*clkCorr);
55}
56
57//
58////////////////////////////////////////////////////////////////////////////
59t_irc t_eph::getCrd(const bncTime& tt, ColumnVector& xc, ColumnVector& vv, bool useCorr) const {
60
61 if (_checkState == bad ||
62 _checkState == unhealthy ||
63 _checkState == outdated) {
64 return failure;
65 }
66
67 xc.ReSize(6);
68 vv.ReSize(3);
69 if (position(tt.gpsw(), tt.gpssec(), xc.data(), vv.data()) != success) {
70 return failure;
71 }
72 if (useCorr) {
73 if (_orbCorr && _clkCorr) {
74 double dtO = tt - _orbCorr->_time;
75 if (_orbCorr->_updateInt) {
76 dtO -= (0.5 * ssrUpdateInt[_orbCorr->_updateInt]);
77 }
78 ColumnVector dx(3);
79 dx[0] = _orbCorr->_xr[0] + _orbCorr->_dotXr[0] * dtO;
80 dx[1] = _orbCorr->_xr[1] + _orbCorr->_dotXr[1] * dtO;
81 dx[2] = _orbCorr->_xr[2] + _orbCorr->_dotXr[2] * dtO;
82
83 RSW_to_XYZ(xc.Rows(1,3), vv.Rows(1,3), dx, dx);
84
85 xc[0] -= dx[0];
86 xc[1] -= dx[1];
87 xc[2] -= dx[2];
88
89 ColumnVector dv(3);
90 RSW_to_XYZ(xc.Rows(1,3), vv.Rows(1,3), _orbCorr->_dotXr, dv);
91
92 vv[0] -= dv[0];
93 vv[1] -= dv[1];
94 vv[2] -= dv[2];
95
96 double dtC = tt - _clkCorr->_time;
97 if (_clkCorr->_updateInt) {
98 dtC -= (0.5 * ssrUpdateInt[_clkCorr->_updateInt]);
99 }
100 xc[3] += _clkCorr->_dClk + _clkCorr->_dotDClk * dtC + _clkCorr->_dotDotDClk * dtC * dtC;
101 }
102 else {
103 return failure;
104 }
105 }
106 return success;
107}
108
109
110//
111//////////////////////////////////////////////////////////////////////////////
112t_irc t_eph::setNavType(QString navTypeStr) {
113 if (navTypeStr == "LNAV") {
114 _navType = t_eph::LNAV;
115 }
116 else if (navTypeStr == "FDMA") {
117 _navType = t_eph::FDMA;
118 }
119 else if (navTypeStr == "FNAV") {
120 _navType = t_eph::FNAV;
121 }
122 else if (navTypeStr == "INAV") {
123 _navType = t_eph::INAF;
124 }
125 else if (navTypeStr == "D1") {
126 _navType = t_eph::D1;
127 }
128 else if (navTypeStr == "D2") {
129 _navType = t_eph::D2;
130 }
131 else if (navTypeStr == "SBAS") {
132 _navType = t_eph::SBASL1;
133 }
134 else if (navTypeStr == "CNAV") {
135 _navType = t_eph::CNAV;
136 }
137 else if (navTypeStr == "CNV1") {
138 _navType = t_eph::CNV1;
139 }
140 else if (navTypeStr == "CNV2") {
141 _navType = t_eph::CNV2;
142 }
143 else if (navTypeStr == "CNV3") {
144 _navType = t_eph::CNV3;
145 }
146 else {
147 _navType = t_eph::undefined;
148 return failure;
149 }
150
151 return success;
152}
153
154//
155//////////////////////////////////////////////////////////////////////////////
156QString t_eph::navTypeString(e_navType navType, const t_prn& prn, double version) {
157 QString navTypeString = "";
158 QString epochStart;
159 QString eolStr;
160
161 if (version < 4.0) {
162 return navTypeString;
163 }
164
165 if (version == 99.0) {
166 epochStart = "";
167 eolStr = "";
168 }
169 else {
170 epochStart = "> ";
171 eolStr = "\n";
172 }
173
174 QString ephStr = QString("EPH %1 ").arg(prn.toString().c_str());
175 switch (navType) {
176 case undefined:
177 navTypeString = epochStart + ephStr + "unknown" + eolStr;
178 break;
179 case LNAV:
180 navTypeString = epochStart + ephStr + "LNAV" + eolStr;
181 break;
182 case FDMA:
183 navTypeString = epochStart + ephStr + "FDMA" + eolStr;
184 break;
185 case FNAV:
186 navTypeString = epochStart + ephStr + "FNAV" + eolStr;
187 break;
188 case INAF:
189 navTypeString = epochStart + ephStr + "INAV" + eolStr;
190 break;
191 case D1:
192 navTypeString = epochStart + ephStr + "D1 " + eolStr;
193 break;
194 case D2:
195 navTypeString = epochStart + ephStr + "D2 " + eolStr;
196 break;
197 case SBASL1:
198 navTypeString = epochStart + ephStr + "SBAS" + eolStr;
199 break;
200 case CNAV:
201 navTypeString = epochStart + ephStr + "CNAV" + eolStr;
202 break;
203 case CNV1:
204 navTypeString = epochStart + ephStr + "CNV1" + eolStr;
205 break;
206 case CNV2:
207 navTypeString = epochStart + ephStr + "CNV2" + eolStr;
208 break;
209 case CNV3:
210 navTypeString = epochStart + ephStr + "CNV3" + eolStr;
211 break;
212 }
213 return navTypeString;
214 }
215
216//
217//////////////////////////////////////////////////////////////////////////////
218QString t_eph::rinexDateStr(const bncTime& tt, const t_prn& prn, double version) {
219 QString prnStr(prn.toString().c_str());
220 return rinexDateStr(tt, prnStr, version);
221}
222
223//
224//////////////////////////////////////////////////////////////////////////////
225QString t_eph::rinexDateStr(const bncTime& tt, const QString& prnStr, double version) {
226
227 QString datStr;
228
229 unsigned year, month, day, hour, min;
230 double sec;
231 tt.civil_date(year, month, day);
232 tt.civil_time(hour, min, sec);
233
234 QTextStream out(&datStr);
235
236 if (version < 3.0) {
237 QString prnHlp = prnStr.mid(1,2); if (prnHlp[0] == '0') prnHlp[0] = ' ';
238 out << prnHlp << QString(" %1 %2 %3 %4 %5%6")
239 .arg(year % 100, 2, 10, QChar('0'))
240 .arg(month, 2)
241 .arg(day, 2)
242 .arg(hour, 2)
243 .arg(min, 2)
244 .arg(sec, 5, 'f',1);
245 }
246 else if (version == 99) {
247 out << QString(" %1 %2 %3 %4 %5 %6")
248 .arg(year, 4)
249 .arg(month, 2, 10, QChar('0'))
250 .arg(day, 2, 10, QChar('0'))
251 .arg(hour, 2, 10, QChar('0'))
252 .arg(min, 2, 10, QChar('0'))
253 .arg(int(sec), 2, 10, QChar('0'));
254 }
255 else {
256 out << prnStr << QString(" %1 %2 %3 %4 %5 %6")
257 .arg(year, 4)
258 .arg(month, 2, 10, QChar('0'))
259 .arg(day, 2, 10, QChar('0'))
260 .arg(hour, 2, 10, QChar('0'))
261 .arg(min, 2, 10, QChar('0'))
262 .arg(int(sec), 2, 10, QChar('0'));
263 }
264
265 return datStr;
266}
267
268// Constructor
269//////////////////////////////////////////////////////////////////////////////
270t_ephGPS::t_ephGPS(double rnxVersion, const QStringList& lines) {
271
272 int nLines = 8;
273
274 if (navType() == t_eph::CNAV) {
275 nLines += 1;
276 }
277 if (navType() == t_eph::CNV2) {
278 nLines += 2;
279 }
280
281 if (lines.size() != nLines) {
282 _checkState = bad;
283 return;
284 }
285
286 // RINEX Format
287 // ------------
288 int fieldLen = 19;
289
290 int pos[4];
291 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
292 pos[1] = pos[0] + fieldLen;
293 pos[2] = pos[1] + fieldLen;
294 pos[3] = pos[2] + fieldLen;
295
296 // Read eight lines
297 // ----------------
298 for (int iLine = 0; iLine < nLines; iLine++) {
299 QString line = lines[iLine];
300
301 if ( iLine == 0 ) {
302 QTextStream in(line.left(pos[1]).toLatin1());
303 int year, month, day, hour, min;
304 double sec;
305
306 QString prnStr, n;
307 in >> prnStr;
308
309 if (prnStr.size() == 1 &&
310 (prnStr[0] == 'G' ||
311 prnStr[0] == 'J' ||
312 prnStr[0] == 'I')) {
313 in >> n;
314 prnStr.append(n);
315 }
316
317 in >> year >> month >> day >> hour >> min >> sec;
318 if (prnStr.at(0) == 'G') {
319 _prn.set('G', prnStr.mid(1).toInt());
320 }
321 else if (prnStr.at(0) == 'J') {
322 _prn.set('J', prnStr.mid(1).toInt());
323 }
324 else if (prnStr.at(0) == 'I') {
325 _prn.set('I', prnStr.mid(1).toInt());
326 }
327 else {
328 _prn.set('G', prnStr.toInt());
329 }
330
331 if (year < 80) {
332 year += 2000;
333 }
334 else if (year < 100) {
335 year += 1900;
336 }
337
338 _TOC.set(year, month, day, hour, min, sec);
339
340 if ( readDbl(line, pos[1], fieldLen, _clock_bias ) ||
341 readDbl(line, pos[2], fieldLen, _clock_drift ) ||
342 readDbl(line, pos[3], fieldLen, _clock_driftrate) ) {
343 _checkState = bad;
344 return;
345 }
346 }
347 // =====================
348 // BROADCAST ORBIT - 1
349 // =====================
350 else if ( iLine == 1) {
351
352 if (navType() == t_eph::CNAV ||
353 navType() == t_eph::CNV2) {
354 if ( readDbl(line, pos[0], fieldLen, _ADOT ) ||
355 readDbl(line, pos[1], fieldLen, _Crs ) ||
356 readDbl(line, pos[2], fieldLen, _Delta_n) ||
357 readDbl(line, pos[3], fieldLen, _M0 ) ) {
358 _checkState = bad;
359 return;
360 }
361 }
362 else { // LNAV, undefined
363 if ( readDbl(line, pos[0], fieldLen, _IODE ) ||
364 readDbl(line, pos[1], fieldLen, _Crs ) ||
365 readDbl(line, pos[2], fieldLen, _Delta_n) ||
366 readDbl(line, pos[3], fieldLen, _M0 ) ) {
367 _checkState = bad;
368 return;
369 }
370 }
371 }
372 // =====================
373 // BROADCAST ORBIT - 2
374 // =====================
375 else if ( iLine == 2 ) {
376 if ( readDbl(line, pos[0], fieldLen, _Cuc ) ||
377 readDbl(line, pos[1], fieldLen, _e ) ||
378 readDbl(line, pos[2], fieldLen, _Cus ) ||
379 readDbl(line, pos[3], fieldLen, _sqrt_A) ) {
380 _checkState = bad;
381 return;
382 }
383 }
384 // =====================
385 // BROADCAST ORBIT - 3
386 // =====================
387 else if ( iLine == 3 ) {
388
389 if (navType() == t_eph::CNAV ||
390 navType() == t_eph::CNV2) {
391 if ( readDbl(line, pos[0], fieldLen, _top) ||
392 readDbl(line, pos[1], fieldLen, _Cic ) ||
393 readDbl(line, pos[2], fieldLen, _OMEGA0) ||
394 readDbl(line, pos[3], fieldLen, _Cis ) ) {
395 _checkState = bad;
396 return;
397 }
398 }
399 else { // LNAV, undefined
400 if ( readDbl(line, pos[0], fieldLen, _TOEsec) ||
401 readDbl(line, pos[1], fieldLen, _Cic ) ||
402 readDbl(line, pos[2], fieldLen, _OMEGA0) ||
403 readDbl(line, pos[3], fieldLen, _Cis ) ) {
404 _checkState = bad;
405 return;
406 }
407 }
408 }
409 // =====================
410 // BROADCAST ORBIT - 4
411 // =====================
412 else if ( iLine == 4 ) {
413 if ( readDbl(line, pos[0], fieldLen, _i0 ) ||
414 readDbl(line, pos[1], fieldLen, _Crc ) ||
415 readDbl(line, pos[2], fieldLen, _omega ) ||
416 readDbl(line, pos[3], fieldLen, _OMEGADOT) ) {
417 _checkState = bad;
418 return;
419 }
420 }
421 // =====================
422 // BROADCAST ORBIT - 5
423 // =====================
424 else if ( iLine == 5 && type() != t_eph::IRNSS) {
425
426 if (navType() == t_eph::CNAV ||
427 navType() == t_eph::CNV2) {
428 if ( readDbl(line, pos[0], fieldLen, _IDOT ) ||
429 readDbl(line, pos[1], fieldLen, _Delta_n_dot) ||
430 readDbl(line, pos[2], fieldLen, _URAI_NED0 ) ||
431 readDbl(line, pos[3], fieldLen, _URAI_NED1) ) {
432 _checkState = bad;
433 return;
434 }
435 }
436 else { // LNAV, undefined
437 if ( readDbl(line, pos[0], fieldLen, _IDOT ) ||
438 readDbl(line, pos[1], fieldLen, _L2Codes) ||
439 readDbl(line, pos[2], fieldLen, _TOEweek) ||
440 readDbl(line, pos[3], fieldLen, _L2PFlag) ) {
441 _checkState = bad;
442 return;
443 }
444 }
445 }
446 else if ( iLine == 5 && type() == t_eph::IRNSS) {
447 if ( readDbl(line, pos[0], fieldLen, _IDOT ) ||
448 readDbl(line, pos[2], fieldLen, _TOEweek) ) {
449 _checkState = bad;
450 return;
451 }
452 }
453 // =====================
454 // BROADCAST ORBIT - 6
455 // =====================
456 else if ( iLine == 6 && type() != t_eph::IRNSS) {
457
458 if (navType() == t_eph::CNAV ||
459 navType() == t_eph::CNV2 ) {
460 if ( readDbl(line, pos[0], fieldLen, _URAI_ED) ||
461 readDbl(line, pos[1], fieldLen, _health ) ||
462 readDbl(line, pos[2], fieldLen, _TGD ) ||
463 readDbl(line, pos[3], fieldLen, _URAI_NED2) ) {
464 _checkState = bad;
465 return;
466 }
467 }
468 else { // LNAV, undefined
469 if ( readDbl(line, pos[0], fieldLen, _ura ) ||
470 readDbl(line, pos[1], fieldLen, _health) ||
471 readDbl(line, pos[2], fieldLen, _TGD ) ||
472 readDbl(line, pos[3], fieldLen, _IODC ) ) {
473 _checkState = bad;
474 return;
475 }
476 }
477 }
478 else if ( iLine == 6 && type() == t_eph::IRNSS) {
479 if ( readDbl(line, pos[0], fieldLen, _ura ) ||
480 readDbl(line, pos[1], fieldLen, _health) ||
481 readDbl(line, pos[2], fieldLen, _TGD ) ) {
482 _checkState = bad;
483 return;
484 }
485 }
486 // =====================
487 // BROADCAST ORBIT - 7
488 // =====================
489 else if ( iLine == 7 ) {
490 if (navType() == t_eph::LNAV ||
491 navType() == t_eph::undefined) {
492
493 if ( readDbl(line, pos[0], fieldLen, _TOT) ) {
494 _checkState = bad;
495 return;
496 }
497
498 if (type() != t_eph::IRNSS) {
499 double fitIntervalRnx;
500 if ( readDbl(line, pos[1], fieldLen, fitIntervalRnx) ) {
501 _checkState = bad;
502 return;
503 }
504 if (type() == t_eph::GPS) { // in RINEX specified always as time period for GPS
505 _fitInterval = fitIntervalRnx;
506 } else if (type() == t_eph::QZSS) { // specified as flag for QZSS
507 if (rnxVersion == 3.02) {
508 _fitInterval = fitIntervalRnx; // specified as time period
509 }
510 else {
511 _fitInterval = fitIntervalFromFlag(fitIntervalRnx, _IODC, t_eph::QZSS);
512 }
513 }
514 }
515 }
516
517 else if (navType() == t_eph::CNAV ||
518 navType() == t_eph::CNV2) {
519 if ( readDbl(line, pos[0], fieldLen, _ISC_L1CA) ||
520 readDbl(line, pos[1], fieldLen, _ISC_L2C ) ||
521 readDbl(line, pos[2], fieldLen, _ISC_L5I5) ||
522 readDbl(line, pos[3], fieldLen, _ISC_L5Q5) ) {
523 _checkState = bad;
524 return;
525 }
526 }
527
528 }
529 // =====================
530 // BROADCAST ORBIT - 8
531 // =====================
532 else if ( iLine == 8 ) {
533 if (navType() == t_eph::CNAV) {
534 if ( readDbl(line, pos[0], fieldLen, _TOT) ||
535 readDbl(line, pos[1], fieldLen, _wnop) ) {
536 _checkState = bad;
537 return;
538 }
539 }
540 else if (navType() == t_eph::CNV2) {
541 if ( readDbl(line, pos[0], fieldLen, _ISC_L1Cd) ||
542 readDbl(line, pos[1], fieldLen, _ISC_L1Cp)) {
543 _checkState = bad;
544 return;
545 }
546 }
547 }
548 // =====================
549 // BROADCAST ORBIT - 9
550 // =====================
551 else if ( iLine == 9 ) {
552 if (navType() == t_eph::CNV2) {
553 if ( readDbl(line, pos[0], fieldLen, _TOT) ||
554 readDbl(line, pos[1], fieldLen, _wnop) ) {
555 _checkState = bad;
556 return;
557 }
558 }
559 }
560 }
561}
562
563// Compute GPS Satellite Position (virtual)
564////////////////////////////////////////////////////////////////////////////
565t_irc t_ephGPS::position(int GPSweek, double GPSweeks, double* xc, double* vv) const {
566
567 static const double omegaEarth = 7292115.1467e-11;
568 static const double gmGRS = 398.6005e12;
569
570 memset(xc, 0, 6*sizeof(double));
571 memset(vv, 0, 3*sizeof(double));
572
573 double a0 = _sqrt_A * _sqrt_A;
574 if (a0 == 0) {
575 return failure;
576 }
577
578 double n0 = sqrt(gmGRS/(a0*a0*a0));
579
580 bncTime tt(GPSweek, GPSweeks);
581 double tk = tt - bncTime(int(_TOEweek), _TOEsec);
582
583 double n = n0 + _Delta_n;
584 double M = _M0 + n*tk;
585 double E = M;
586 double E_last;
587 int nLoop = 0;
588 do {
589 E_last = E;
590 E = M + _e*sin(E);
591
592 if (++nLoop == 100) {
593 return failure;
594 }
595 } while ( fabs(E-E_last)*a0 > 0.001);
596 double v = 2.0*atan( sqrt( (1.0 + _e)/(1.0 - _e) )*tan( E/2 ) );
597 double u0 = v + _omega;
598 double sin2u0 = sin(2*u0);
599 double cos2u0 = cos(2*u0);
600 double r = a0*(1 - _e*cos(E)) + _Crc*cos2u0 + _Crs*sin2u0;
601 double i = _i0 + _IDOT*tk + _Cic*cos2u0 + _Cis*sin2u0;
602 double u = u0 + _Cuc*cos2u0 + _Cus*sin2u0;
603 double xp = r*cos(u);
604 double yp = r*sin(u);
605 double OM = _OMEGA0 + (_OMEGADOT - omegaEarth)*tk -
606 omegaEarth*_TOEsec;
607
608 double sinom = sin(OM);
609 double cosom = cos(OM);
610 double sini = sin(i);
611 double cosi = cos(i);
612 xc[0] = xp*cosom - yp*cosi*sinom;
613 xc[1] = xp*sinom + yp*cosi*cosom;
614 xc[2] = yp*sini;
615
616 double tc = tt - _TOC;
617 xc[3] = _clock_bias + _clock_drift*tc + _clock_driftrate*tc*tc;
618
619 // Velocity
620 // --------
621 double tanv2 = tan(v/2);
622 double dEdM = 1 / (1 - _e*cos(E));
623 double dotv = sqrt((1.0 + _e)/(1.0 - _e)) / cos(E/2)/cos(E/2) / (1 + tanv2*tanv2)
624 * dEdM * n;
625 double dotu = dotv + (-_Cuc*sin2u0 + _Cus*cos2u0)*2*dotv;
626 double dotom = _OMEGADOT - omegaEarth;
627 double doti = _IDOT + (-_Cic*sin2u0 + _Cis*cos2u0)*2*dotv;
628 double dotr = a0 * _e*sin(E) * dEdM * n
629 + (-_Crc*sin2u0 + _Crs*cos2u0)*2*dotv;
630 double dotx = dotr*cos(u) - r*sin(u)*dotu;
631 double doty = dotr*sin(u) + r*cos(u)*dotu;
632
633 vv[0] = cosom *dotx - cosi*sinom *doty // dX / dr
634 - xp*sinom*dotom - yp*cosi*cosom*dotom // dX / dOMEGA
635 + yp*sini*sinom*doti; // dX / di
636
637 vv[1] = sinom *dotx + cosi*cosom *doty
638 + xp*cosom*dotom - yp*cosi*sinom*dotom
639 - yp*sini*cosom*doti;
640
641 vv[2] = sini *doty + yp*cosi *doti;
642
643 // Relativistic Correction
644 // -----------------------
645 xc[3] -= 4.442807633e-10 * _e * sqrt(a0) *sin(E);
646
647 xc[4] = _clock_drift + _clock_driftrate*tc;
648 xc[5] = _clock_driftrate;
649
650 return success;
651}
652
653// RINEX Format String
654//////////////////////////////////////////////////////////////////////////////
655QString t_ephGPS::toString(double version) const {
656
657 QString navStr = navTypeString(_navType, _prn, version);
658 QString rnxStr = navStr + rinexDateStr(_TOC, _prn, version);
659
660 QTextStream out(&rnxStr);
661
662 out << QString("%1%2%3\n")
663 .arg(_clock_bias, 19, 'e', 12)
664 .arg(_clock_drift, 19, 'e', 12)
665 .arg(_clock_driftrate, 19, 'e', 12);
666
667 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
668
669
670 // =====================
671 // BROADCAST ORBIT - 1
672 // =====================
673 if (navType() == t_eph::CNAV ||
674 navType() == t_eph::CNV2) {
675 out << QString(fmt)
676 .arg(_ADOT, 19, 'e', 12)
677 .arg(_Crs, 19, 'e', 12)
678 .arg(_Delta_n, 19, 'e', 12)
679 .arg(_M0, 19, 'e', 12);
680 }
681 else { // LNAV, undefinded
682 out << QString(fmt)
683 .arg(_IODE, 19, 'e', 12)
684 .arg(_Crs, 19, 'e', 12)
685 .arg(_Delta_n, 19, 'e', 12)
686 .arg(_M0, 19, 'e', 12);
687 }
688 // =====================
689 // BROADCAST ORBIT - 2
690 // =====================
691 out << QString(fmt)
692 .arg(_Cuc, 19, 'e', 12)
693 .arg(_e, 19, 'e', 12)
694 .arg(_Cus, 19, 'e', 12)
695 .arg(_sqrt_A, 19, 'e', 12);
696 // =====================
697 // BROADCAST ORBIT - 3
698 // =====================
699 if (navType() == t_eph::CNAV ||
700 navType() == t_eph::CNV2) {
701 out << QString(fmt)
702 .arg(_top, 19, 'e', 12)
703 .arg(_Cic, 19, 'e', 12)
704 .arg(_OMEGA0, 19, 'e', 12)
705 .arg(_Cis, 19, 'e', 12);
706 }
707 else {
708 out << QString(fmt)
709 .arg(_TOEsec, 19, 'e', 12)
710 .arg(_Cic, 19, 'e', 12)
711 .arg(_OMEGA0, 19, 'e', 12)
712 .arg(_Cis, 19, 'e', 12);
713 }
714 // =====================
715 // BROADCAST ORBIT - 4
716 // =====================
717 out << QString(fmt)
718 .arg(_i0, 19, 'e', 12)
719 .arg(_Crc, 19, 'e', 12)
720 .arg(_omega, 19, 'e', 12)
721 .arg(_OMEGADOT, 19, 'e', 12);
722 // =====================
723 // BROADCAST ORBIT - 5
724 // =====================
725 if (type() == t_eph::IRNSS) {
726 out << QString(fmt)
727 .arg(_IDOT, 19, 'e', 12)
728 .arg(0.0, 19, 'e', 12)
729 .arg(_TOEweek, 19, 'e', 12)
730 .arg(0.0, 19, 'e', 12);
731 }
732 else {
733 if (navType() == t_eph::CNAV ||
734 navType() == t_eph::CNV2) {
735 out << QString(fmt)
736 .arg(_IDOT, 19, 'e', 12)
737 .arg(_Delta_n_dot, 19, 'e', 12)
738 .arg(_URAI_NED0, 19, 'e', 12)
739 .arg(_URAI_NED1, 19, 'e', 12);
740
741 }
742 else { // LNAV, undefined
743 out << QString(fmt)
744 .arg(_IDOT, 19, 'e', 12)
745 .arg(_L2Codes, 19, 'e', 12)
746 .arg(_TOEweek, 19, 'e', 12)
747 .arg(_L2PFlag, 19, 'e', 12);
748
749 }
750 }
751 // =====================
752 // BROADCAST ORBIT - 6
753 // =====================
754 if (type() == t_eph::IRNSS) {
755 out << QString(fmt)
756 .arg(_ura, 19, 'e', 12)
757 .arg(_health, 19, 'e', 12)
758 .arg(_TGD, 19, 'e', 12)
759 .arg(0.0, 19, 'e', 12);
760 }
761 else {
762 if (navType() == t_eph::CNAV ||
763 navType() == t_eph::CNV2) {
764 out << QString(fmt)
765 .arg(_URAI_ED, 19, 'e', 12)
766 .arg(_health, 19, 'e', 12)
767 .arg(_TGD, 19, 'e', 12)
768 .arg(_URAI_NED2,19, 'e', 12);
769
770 }
771 else { // LNAV, undefined
772 out << QString(fmt)
773 .arg(_ura, 19, 'e', 12)
774 .arg(_health, 19, 'e', 12)
775 .arg(_TGD, 19, 'e', 12)
776 .arg(_IODC, 19, 'e', 12);
777 }
778 }
779 // =====================
780 // BROADCAST ORBIT - 7
781 // =====================
782 if (navType() == t_eph::LNAV ||
783 navType() == t_eph::undefined) {
784
785 double tot = _TOT;
786 if (tot == 0.9999e9 && version < 3.0) {
787 tot = 0.0;
788 }
789 // fitInterval
790 if (type() == t_eph::IRNSS) {// not valid for IRNSS
791 out << QString(fmt)
792 .arg(tot, 19, 'e', 12)
793 .arg(0.0, 19, 'e', 12)
794 .arg("", 19, QChar(' '))
795 .arg("", 19, QChar(' '));
796 }
797 else {
798 // for GPS and QZSS in version 3.02 specified in hours
799 double fitIntervalRnx = _fitInterval;
800 // otherwise specified as flag
801 if (type() == t_eph::QZSS && version != 3.02) {
802 (_fitInterval == 2.0) ? fitIntervalRnx = 0.0 : fitIntervalRnx = 1.0;
803 }
804 out << QString(fmt)
805 .arg(tot, 19, 'e', 12)
806 .arg(fitIntervalRnx, 19, 'e', 12)
807 .arg("", 19, QChar(' '))
808 .arg("", 19, QChar(' '));
809 }
810 }
811 else if (navType() == t_eph::CNAV ||
812 navType() == t_eph::CNV2) {
813 out << QString(fmt)
814 .arg(_ISC_L1CA, 19, 'e', 12)
815 .arg(_ISC_L2C, 19, 'e', 12)
816 .arg(_ISC_L5I5, 19, 'e', 12)
817 .arg(_ISC_L5Q5, 19, 'e', 12);
818 }
819 // =====================
820 // BROADCAST ORBIT - 8
821 // =====================
822 if (navType() == t_eph::CNAV) {
823 out << QString(fmt)
824 .arg(_TOT, 19, 'e', 12)
825 .arg(_wnop, 19, 'e', 12)
826 .arg("", 19, QChar(' '))
827 .arg("", 19, QChar(' '));
828 }
829 else if (navType() == t_eph::CNV2) {
830 out << QString(fmt)
831 .arg(_ISC_L1Cd, 19, 'e', 12)
832 .arg(_ISC_L1Cp, 19, 'e', 12)
833 .arg("", 19, QChar(' '))
834 .arg("", 19, QChar(' '));
835 }
836
837 // =====================
838 // BROADCAST ORBIT - 9
839 // =====================
840 if (navType() == t_eph::CNV2) {
841 out << QString(fmt)
842 .arg(_TOT, 19, 'e', 12)
843 .arg(_wnop, 19, 'e', 12)
844 .arg("", 19, QChar(' '))
845 .arg("", 19, QChar(' '));
846 }
847
848 return rnxStr;
849}
850
851// Constructor
852//////////////////////////////////////////////////////////////////////////////
853t_ephGlo::t_ephGlo(double rnxVersion, const QStringList& lines) {
854
855 int nLines = 4;
856 if (rnxVersion >= 3.05) {
857 nLines += 1;
858 }
859 else {
860 _M_delta_tau = 0.9999e9; // unknown
861 _M_FT = 1.5e1; // unknown
862 }
863
864 if (lines.size() != nLines) {
865 _checkState = bad;
866 return;
867 }
868
869 // RINEX Format
870 // ------------
871 int fieldLen = 19;
872 double statusflags = 0.0;
873 double healthflags = 0.0;
874
875 int pos[4];
876 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
877 pos[1] = pos[0] + fieldLen;
878 pos[2] = pos[1] + fieldLen;
879 pos[3] = pos[2] + fieldLen;
880
881 // Read four lines
882 // ---------------
883 for (int iLine = 0; iLine < nLines; iLine++) {
884 QString line = lines[iLine];
885
886 if ( iLine == 0 ) {
887 QTextStream in(line.left(pos[1]).toLatin1());
888
889 int year, month, day, hour, min;
890 double sec;
891
892 QString prnStr, n;
893 in >> prnStr;
894 if (prnStr.size() == 1 && prnStr[0] == 'R') {
895 in >> n;
896 prnStr.append(n);
897 }
898 in >> year >> month >> day >> hour >> min >> sec;
899 if (prnStr.at(0) == 'R') {
900 _prn.set('R', prnStr.mid(1).toInt());
901 }
902 else {
903 _prn.set('R', prnStr.toInt());
904 }
905
906 if (year < 80) {
907 year += 2000;
908 }
909 else if (year < 100) {
910 year += 1900;
911 }
912
913 _gps_utc = gnumleap(year, month, day);
914
915 _TOC.set(year, month, day, hour, min, sec);
916 _TOC = _TOC + _gps_utc;
917 int nd = int((_TOC.gpssec())) / (24.0*60.0*60.0);
918 if ( readDbl(line, pos[1], fieldLen, _tau ) ||
919 readDbl(line, pos[2], fieldLen, _gamma) ||
920 readDbl(line, pos[3], fieldLen, _tki ) ) {
921 _checkState = bad;
922 return;
923 }
924 _tki -= nd * 86400.0;
925 _tau = -_tau;
926 }
927 // =====================
928 // BROADCAST ORBIT - 1
929 // =====================
930 else if ( iLine == 1 ) {
931 if ( readDbl(line, pos[0], fieldLen, _x_pos ) ||
932 readDbl(line, pos[1], fieldLen, _x_velocity ) ||
933 readDbl(line, pos[2], fieldLen, _x_acceleration) ||
934 readDbl(line, pos[3], fieldLen, _health ) ) {
935 _checkState = bad;
936 return;
937 }
938 }
939 // =====================
940 // BROADCAST ORBIT - 2
941 // =====================
942 else if ( iLine == 2 ) {
943 if ( readDbl(line, pos[0], fieldLen, _y_pos ) ||
944 readDbl(line, pos[1], fieldLen, _y_velocity ) ||
945 readDbl(line, pos[2], fieldLen, _y_acceleration ) ||
946 readDbl(line, pos[3], fieldLen, _frequency_number) ) {
947 _checkState = bad;
948 return;
949 }
950 }
951 // =====================
952 // BROADCAST ORBIT - 3
953 // =====================
954 else if ( iLine == 3 ) {
955 if ( readDbl(line, pos[0], fieldLen, _z_pos ) ||
956 readDbl(line, pos[1], fieldLen, _z_velocity ) ||
957 readDbl(line, pos[2], fieldLen, _z_acceleration) ||
958 readDbl(line, pos[3], fieldLen, _E ) ) {
959 _checkState = bad;
960 return;
961 }
962 }
963 // =====================
964 // BROADCAST ORBIT - 4
965 // =====================
966 else if ( iLine == 4 ) {
967 if (readDbl(line, pos[0], fieldLen, statusflags ) ) {
968 //statusflags BLK, do nothing
969 _flags_unknown = true;
970 }
971 else {
972 _flags_unknown = false;
973 // status flags
974 // ============
975 // bit 0-1
976 _M_P = double(bitExtracted(statusflags, 2, 0));
977 // bit 2-3
978 _P1 = double(bitExtracted(statusflags, 2, 2));
979 // bit 4
980 _P2 = double(bitExtracted(statusflags, 1, 4));
981 // bit 5
982 _P3 = double(bitExtracted(statusflags, 1, 5));
983 // bit 6
984 _M_P4 = double(bitExtracted(statusflags, 1, 6));
985 // bit 7-8
986 _M_M = double(bitExtracted(statusflags, 2, 7));
987 /// GLO M/K exclusive flags/values only valid if flag M is set to '01'
988 if (!_M_M) {
989 _M_P4 = 0.0;
990 _M_P = 0.0;
991 }
992 }
993 if ( readDbl(line, pos[1], fieldLen, _M_delta_tau ) ||
994 readDbl(line, pos[2], fieldLen, _M_FT ) ) {
995 _checkState = bad;
996 return;
997 }
998 if (readDbl(line, pos[3], fieldLen, healthflags ) ) {
999 // healthflags BLK
1000 _flags_unknown = true;
1001 }
1002 else {
1003 _flags_unknown = false;
1004 // health flags
1005 // ============
1006 // bit 0 (is to be ignored, if bit 1 is zero)
1007 _almanac_health = double(bitExtracted(healthflags, 1, 0));
1008 // bit 1
1009 _almanac_health_availablility_indicator = double(bitExtracted(healthflags, 1, 1));
1010 // bit 2
1011 _M_l3 = double(bitExtracted(healthflags, 1, 2));
1012 }
1013 }
1014 }
1015
1016 // Initialize status vector
1017 // ------------------------
1018 _tt = _TOC;
1019 _xv.ReSize(6); _xv = 0.0;
1020 _xv(1) = _x_pos * 1.e3;
1021 _xv(2) = _y_pos * 1.e3;
1022 _xv(3) = _z_pos * 1.e3;
1023 _xv(4) = _x_velocity * 1.e3;
1024 _xv(5) = _y_velocity * 1.e3;
1025 _xv(6) = _z_velocity * 1.e3;
1026}
1027
1028// Compute Glonass Satellite Position (virtual)
1029////////////////////////////////////////////////////////////////////////////
1030t_irc t_ephGlo::position(int GPSweek, double GPSweeks, double* xc, double* vv) const {
1031
1032 static const double nominalStep = 10.0;
1033
1034 memset(xc, 0, 6*sizeof(double));
1035 memset(vv, 0, 3*sizeof(double));
1036
1037 double dtPos = bncTime(GPSweek, GPSweeks) - _tt;
1038
1039 if (fabs(dtPos) > 24 * 3600.0) {
1040 return failure;
1041 }
1042
1043 int nSteps = int(fabs(dtPos) / nominalStep) + 1;
1044 double step = dtPos / nSteps;
1045
1046 double acc[3];
1047 acc[0] = _x_acceleration * 1.e3;
1048 acc[1] = _y_acceleration * 1.e3;
1049 acc[2] = _z_acceleration * 1.e3;
1050
1051 for (int ii = 1; ii <= nSteps; ii++) {
1052 _xv = rungeKutta4(_tt.gpssec(), _xv, step, acc, glo_deriv);
1053 _tt = _tt + step;
1054 }
1055
1056 // Position and Velocity
1057 // ---------------------
1058 xc[0] = _xv(1);
1059 xc[1] = _xv(2);
1060 xc[2] = _xv(3);
1061
1062 vv[0] = _xv(4);
1063 vv[1] = _xv(5);
1064 vv[2] = _xv(6);
1065
1066 // Clock Correction
1067 // ----------------
1068 double dtClk = bncTime(GPSweek, GPSweeks) - _TOC;
1069 xc[3] = -_tau + _gamma * dtClk;
1070
1071 xc[4] = _gamma;
1072 xc[5] = 0.0;
1073
1074 return success;
1075}
1076
1077// RINEX Format String
1078//////////////////////////////////////////////////////////////////////////////
1079QString t_ephGlo::toString(double version) const {
1080
1081 QString navStr = navTypeString(_navType, _prn, version);
1082 QString rnxStr = navStr + rinexDateStr(_TOC -_gps_utc, _prn, version);
1083 int nd = int((_TOC - _gps_utc).gpssec()) / (24.0*60.0*60.0);
1084 QTextStream out(&rnxStr);
1085
1086 out << QString("%1%2%3\n")
1087 .arg(-_tau, 19, 'e', 12)
1088 .arg(_gamma, 19, 'e', 12)
1089 .arg(_tki+nd*86400.0, 19, 'e', 12);
1090
1091 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
1092 // =====================
1093 // BROADCAST ORBIT - 1
1094 // =====================
1095 out << QString(fmt)
1096 .arg(_x_pos, 19, 'e', 12)
1097 .arg(_x_velocity, 19, 'e', 12)
1098 .arg(_x_acceleration, 19, 'e', 12)
1099 .arg(_health, 19, 'e', 12);
1100 // =====================
1101 // BROADCAST ORBIT - 2
1102 // =====================
1103 out << QString(fmt)
1104 .arg(_y_pos, 19, 'e', 12)
1105 .arg(_y_velocity, 19, 'e', 12)
1106 .arg(_y_acceleration, 19, 'e', 12)
1107 .arg(_frequency_number, 19, 'e', 12);
1108 // =====================
1109 // BROADCAST ORBIT - 3
1110 // =====================
1111 out << QString(fmt)
1112 .arg(_z_pos, 19, 'e', 12)
1113 .arg(_z_velocity, 19, 'e', 12)
1114 .arg(_z_acceleration, 19, 'e', 12)
1115 .arg(_E, 19, 'e', 12);
1116 // =====================
1117 // BROADCAST ORBIT - 4
1118 // =====================
1119 if (version >= 3.05) {
1120 // unknown (RINEX version < 3.05)
1121 if (_flags_unknown) {
1122 out << QString(fmt)
1123 .arg("", 19, QChar(' ')) // statusflags blank if unknown
1124 .arg(_M_delta_tau, 19, 'e', 12)
1125 .arg(_M_FT, 19, 'e', 12)
1126 .arg("", 19, QChar(' ')); // healthflags blank if unknown
1127 }
1128 else {
1129 int statusflags = 0;
1130 // bit 7-8
1131 if (_M_M == 2.0) {
1132 statusflags |= (1<<7);
1133 }
1134 // bit 6
1135 if (_M_P4) {
1136 statusflags |= (1<<6);
1137 }
1138 // bit 5
1139 if (_P3) {
1140 statusflags |= (1<<5);
1141 }
1142 // bit 4
1143 if (_P2) {
1144 statusflags |= (1<<4);
1145 }
1146 // bit 2-3
1147 if (_P1 == 2.0) {
1148 statusflags |= (1<<2);
1149 }
1150 else if (_P1 == 1.0) {
1151 statusflags |= (1<<3);
1152 }
1153 else if (_P1 == 3.0) {
1154 statusflags |= (1<<2);
1155 statusflags |= (1<<3);
1156 }
1157 // bit 0-1
1158 if (_M_P == 2.0) {
1159 statusflags |= (1<<0);
1160 }
1161 else if (_M_P == 1.0) {
1162 statusflags |= (1<<1);
1163 }
1164 else if (_M_P == 3.0) {
1165 statusflags |= (1<<0);
1166 statusflags |= (1<<1);
1167 }
1168 // health flags
1169 // ============
1170 int healthflags = 0;
1171 // bit 0 (is to be ignored, if bit 1 is zero)
1172 if (_almanac_health) {
1173 healthflags |= (1<<0);
1174 }
1175 // bit 1
1176 if (_almanac_health_availablility_indicator) {
1177 healthflags |= (1<<1);
1178 }
1179 // bit 2
1180 if (_M_l3) {
1181 healthflags |= (1<<2);
1182 }
1183 out << QString(fmt)
1184 .arg(double(statusflags), 19, 'e', 12)
1185 .arg(_M_delta_tau, 19, 'e', 12)
1186 .arg(_M_FT, 19, 'e', 12)
1187 .arg(double(healthflags), 19, 'e', 12);
1188 }
1189 }
1190
1191 return rnxStr;
1192}
1193
1194// Derivative of the state vector using a simple force model (static)
1195////////////////////////////////////////////////////////////////////////////
1196ColumnVector t_ephGlo::glo_deriv(double /* tt */, const ColumnVector& xv,
1197 double* acc) {
1198
1199 // State vector components
1200 // -----------------------
1201 ColumnVector rr = xv.rows(1,3);
1202 ColumnVector vv = xv.rows(4,6);
1203
1204 // Acceleration
1205 // ------------
1206 static const double gmWGS = 398.60044e12;
1207 static const double AE = 6378136.0;
1208 static const double OMEGA = 7292115.e-11;
1209 static const double C20 = -1082.6257e-6;
1210
1211 double rho = rr.NormFrobenius();
1212 double t1 = -gmWGS/(rho*rho*rho);
1213 double t2 = 3.0/2.0 * C20 * (gmWGS*AE*AE) / (rho*rho*rho*rho*rho);
1214 double t3 = OMEGA * OMEGA;
1215 double t4 = 2.0 * OMEGA;
1216 double z2 = rr(3) * rr(3);
1217
1218 // Vector of derivatives
1219 // ---------------------
1220 ColumnVector va(6);
1221 va(1) = vv(1);
1222 va(2) = vv(2);
1223 va(3) = vv(3);
1224 va(4) = (t1 + t2*(1.0-5.0*z2/(rho*rho)) + t3) * rr(1) + t4*vv(2) + acc[0];
1225 va(5) = (t1 + t2*(1.0-5.0*z2/(rho*rho)) + t3) * rr(2) - t4*vv(1) + acc[1];
1226 va(6) = (t1 + t2*(3.0-5.0*z2/(rho*rho)) ) * rr(3) + acc[2];
1227
1228 return va;
1229}
1230
1231// IOD of Glonass Ephemeris (virtual)
1232////////////////////////////////////////////////////////////////////////////
1233unsigned int t_ephGlo::IOD() const {
1234 bncTime tMoscow = _TOC - _gps_utc + 3 * 3600.0;
1235 return (unsigned long)tMoscow.daysec() / 900;
1236}
1237
1238// Health status of Glonass Ephemeris (virtual)
1239////////////////////////////////////////////////////////////////////////////
1240unsigned int t_ephGlo::isUnhealthy() const {
1241
1242 if (_almanac_health_availablility_indicator) {
1243 if ((_health == 0 && _almanac_health == 0) ||
1244 (_health == 1 && _almanac_health == 0) ||
1245 (_health == 1 && _almanac_health == 1)) {
1246 return 1;
1247 }
1248 }
1249 else if (!_almanac_health_availablility_indicator) {
1250 if (_health) {
1251 return 1;
1252 }
1253 }
1254 return 0; /* (_health == 0 && _almanac_health == 1) or (_health == 0) */
1255}
1256
1257// Constructor
1258//////////////////////////////////////////////////////////////////////////////
1259t_ephGal::t_ephGal(double rnxVersion, const QStringList& lines) {
1260 int year, month, day, hour, min;
1261 double sec;
1262 QString prnStr;
1263 const int nLines = 8;
1264 if (lines.size() != nLines) {
1265 _checkState = bad;
1266 return;
1267 }
1268
1269 // RINEX Format
1270 // ------------
1271 int fieldLen = 19;
1272 double SVhealth = 0.0;
1273 double datasource = 0.0;
1274
1275 int pos[4];
1276 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
1277 pos[1] = pos[0] + fieldLen;
1278 pos[2] = pos[1] + fieldLen;
1279 pos[3] = pos[2] + fieldLen;
1280
1281 // Read eight lines
1282 // ----------------
1283 for (int iLine = 0; iLine < nLines; iLine++) {
1284 QString line = lines[iLine];
1285
1286 if ( iLine == 0 ) {
1287 QTextStream in(line.left(pos[1]).toLatin1());
1288 QString n;
1289 in >> prnStr;
1290 if (prnStr.size() == 1 && prnStr[0] == 'E') {
1291 in >> n;
1292 prnStr.append(n);
1293 }
1294 in >> year >> month >> day >> hour >> min >> sec;
1295 if (year < 80) {
1296 year += 2000;
1297 }
1298 else if (year < 100) {
1299 year += 1900;
1300 }
1301
1302 _TOC.set(year, month, day, hour, min, sec);
1303
1304 if ( readDbl(line, pos[1], fieldLen, _clock_bias ) ||
1305 readDbl(line, pos[2], fieldLen, _clock_drift ) ||
1306 readDbl(line, pos[3], fieldLen, _clock_driftrate) ) {
1307 _checkState = bad;
1308 return;
1309 }
1310 }
1311 // =====================
1312 // BROADCAST ORBIT - 1
1313 // =====================
1314 else if ( iLine == 1 ) {
1315 if ( readDbl(line, pos[0], fieldLen, _IODnav ) ||
1316 readDbl(line, pos[1], fieldLen, _Crs ) ||
1317 readDbl(line, pos[2], fieldLen, _Delta_n) ||
1318 readDbl(line, pos[3], fieldLen, _M0 ) ) {
1319 _checkState = bad;
1320 return;
1321 }
1322 }
1323 // =====================
1324 // BROADCAST ORBIT - 2
1325 // =====================
1326 else if ( iLine == 2 ) {
1327 if ( readDbl(line, pos[0], fieldLen, _Cuc ) ||
1328 readDbl(line, pos[1], fieldLen, _e ) ||
1329 readDbl(line, pos[2], fieldLen, _Cus ) ||
1330 readDbl(line, pos[3], fieldLen, _sqrt_A) ) {
1331 _checkState = bad;
1332 return;
1333 }
1334 }
1335 // =====================
1336 // BROADCAST ORBIT - 3
1337 // =====================
1338 else if ( iLine == 3 ) {
1339 if ( readDbl(line, pos[0], fieldLen, _TOEsec) ||
1340 readDbl(line, pos[1], fieldLen, _Cic ) ||
1341 readDbl(line, pos[2], fieldLen, _OMEGA0) ||
1342 readDbl(line, pos[3], fieldLen, _Cis ) ) {
1343 _checkState = bad;
1344 return;
1345 }
1346 }
1347 // =====================
1348 // BROADCAST ORBIT - 4
1349 // =====================
1350 else if ( iLine == 4 ) {
1351 if ( readDbl(line, pos[0], fieldLen, _i0 ) ||
1352 readDbl(line, pos[1], fieldLen, _Crc ) ||
1353 readDbl(line, pos[2], fieldLen, _omega ) ||
1354 readDbl(line, pos[3], fieldLen, _OMEGADOT) ) {
1355 _checkState = bad;
1356 return;
1357 }
1358 }
1359 // =====================
1360 // BROADCAST ORBIT - 5
1361 // =====================
1362 else if ( iLine == 5 ) {
1363 if ( readDbl(line, pos[0], fieldLen, _IDOT ) ||
1364 readDbl(line, pos[1], fieldLen, datasource) ||
1365 readDbl(line, pos[2], fieldLen, _TOEweek ) ) {
1366 _checkState = bad;
1367 return;
1368 } else {
1369 if (int(datasource) & (1<<8)) {
1370 _fnav = true;
1371 _inav = false;
1372 } else if (int(datasource) & (1<<9)) {
1373 _fnav = false;
1374 _inav = true;
1375 }
1376 _TOEweek -= 1024.0;
1377 }
1378 }
1379 // =====================
1380 // BROADCAST ORBIT - 6
1381 // =====================
1382 else if ( iLine == 6 ) {
1383 if ( readDbl(line, pos[0], fieldLen, _SISA ) ||
1384 readDbl(line, pos[1], fieldLen, SVhealth) ||
1385 readDbl(line, pos[2], fieldLen, _BGD_1_5A) ||
1386 readDbl(line, pos[3], fieldLen, _BGD_1_5B) ) {
1387 _checkState = bad;
1388 return;
1389 } else {
1390 // Bit 0
1391 _e1DataInValid = (int(SVhealth) & (1<<0));
1392 // Bit 1-2
1393 _E1_bHS = double((int(SVhealth) >> 1) & 0x3);
1394 // Bit 3
1395 _e5aDataInValid = (int(SVhealth) & (1<<3));
1396 // Bit 4-5
1397 _E5aHS = double((int(SVhealth) >> 4) & 0x3);
1398 // Bit 6
1399 _e5bDataInValid = (int(SVhealth) & (1<<6));
1400 // Bit 7-8
1401 _E5bHS = double((int(SVhealth) >> 7) & 0x3);
1402
1403 if (prnStr.at(0) == 'E') {
1404 _prn.set('E', prnStr.mid(1).toInt(), _inav ? 1 : 0);
1405 }
1406 }
1407 }
1408 // =====================
1409 // BROADCAST ORBIT - 7
1410 // =====================
1411 else if ( iLine == 7 ) {
1412 if ( readDbl(line, pos[0], fieldLen, _TOT) ) {
1413 _checkState = bad;
1414 return;
1415 }
1416 }
1417 }
1418}
1419
1420// Compute Galileo Satellite Position (virtual)
1421////////////////////////////////////////////////////////////////////////////
1422t_irc t_ephGal::position(int GPSweek, double GPSweeks, double* xc, double* vv) const {
1423
1424 static const double omegaEarth = 7292115.1467e-11;
1425 static const double gmWGS = 398.6004418e12;
1426
1427 memset(xc, 0, 6*sizeof(double));
1428 memset(vv, 0, 3*sizeof(double));
1429
1430 double a0 = _sqrt_A * _sqrt_A;
1431 if (a0 == 0) {
1432 return failure;
1433 }
1434
1435 double n0 = sqrt(gmWGS/(a0*a0*a0));
1436
1437 bncTime tt(GPSweek, GPSweeks);
1438 double tk = tt - bncTime(_TOC.gpsw(), _TOEsec);
1439
1440 double n = n0 + _Delta_n;
1441 double M = _M0 + n*tk;
1442 double E = M;
1443 double E_last;
1444 int nLoop = 0;
1445 do {
1446 E_last = E;
1447 E = M + _e*sin(E);
1448
1449 if (++nLoop == 100) {
1450 return failure;
1451 }
1452 } while ( fabs(E-E_last)*a0 > 0.001 );
1453 double v = 2.0*atan( sqrt( (1.0 + _e)/(1.0 - _e) )*tan( E/2 ) );
1454 double u0 = v + _omega;
1455 double sin2u0 = sin(2*u0);
1456 double cos2u0 = cos(2*u0);
1457 double r = a0*(1 - _e*cos(E)) + _Crc*cos2u0 + _Crs*sin2u0;
1458 double i = _i0 + _IDOT*tk + _Cic*cos2u0 + _Cis*sin2u0;
1459 double u = u0 + _Cuc*cos2u0 + _Cus*sin2u0;
1460 double xp = r*cos(u);
1461 double yp = r*sin(u);
1462 double OM = _OMEGA0 + (_OMEGADOT - omegaEarth)*tk -
1463 omegaEarth*_TOEsec;
1464
1465 double sinom = sin(OM);
1466 double cosom = cos(OM);
1467 double sini = sin(i);
1468 double cosi = cos(i);
1469 xc[0] = xp*cosom - yp*cosi*sinom;
1470 xc[1] = xp*sinom + yp*cosi*cosom;
1471 xc[2] = yp*sini;
1472
1473 double tc = tt - _TOC;
1474 xc[3] = _clock_bias + _clock_drift*tc + _clock_driftrate*tc*tc;
1475
1476 // Velocity
1477 // --------
1478 double tanv2 = tan(v/2);
1479 double dEdM = 1 / (1 - _e*cos(E));
1480 double dotv = sqrt((1.0 + _e)/(1.0 - _e)) / cos(E/2)/cos(E/2) / (1 + tanv2*tanv2)
1481 * dEdM * n;
1482 double dotu = dotv + (-_Cuc*sin2u0 + _Cus*cos2u0)*2*dotv;
1483 double dotom = _OMEGADOT - omegaEarth;
1484 double doti = _IDOT + (-_Cic*sin2u0 + _Cis*cos2u0)*2*dotv;
1485 double dotr = a0 * _e*sin(E) * dEdM * n
1486 + (-_Crc*sin2u0 + _Crs*cos2u0)*2*dotv;
1487 double dotx = dotr*cos(u) - r*sin(u)*dotu;
1488 double doty = dotr*sin(u) + r*cos(u)*dotu;
1489
1490 vv[0] = cosom *dotx - cosi*sinom *doty // dX / dr
1491 - xp*sinom*dotom - yp*cosi*cosom*dotom // dX / dOMEGA
1492 + yp*sini*sinom*doti; // dX / di
1493
1494 vv[1] = sinom *dotx + cosi*cosom *doty
1495 + xp*cosom*dotom - yp*cosi*sinom*dotom
1496 - yp*sini*cosom*doti;
1497
1498 vv[2] = sini *doty + yp*cosi *doti;
1499
1500 // Relativistic Correction
1501 // -----------------------
1502 xc[3] -= 4.442807309e-10 * _e * sqrt(a0) *sin(E);
1503
1504 xc[4] = _clock_drift + _clock_driftrate*tc;
1505 xc[5] = _clock_driftrate;
1506
1507 return success;
1508}
1509
1510// Health status of Galileo Ephemeris (virtual)
1511////////////////////////////////////////////////////////////////////////////
1512unsigned int t_ephGal::isUnhealthy() const {
1513 if (_E5aHS || _E5bHS || _E1_bHS) {
1514 return 1;
1515 }
1516 if (_SISA == 255.0) {
1517 return 1;
1518 }
1519 if (_e5aDataInValid || _e5bDataInValid || _e1DataInValid) {
1520 return 1;
1521 }
1522 return 0;
1523}
1524
1525// RINEX Format String
1526//////////////////////////////////////////////////////////////////////////////
1527QString t_ephGal::toString(double version) const {
1528
1529 QString navStr = navTypeString(_navType, _prn, version);
1530 QString rnxStr = navStr + rinexDateStr(_TOC, _prn, version);
1531
1532 QTextStream out(&rnxStr);
1533
1534 out << QString("%1%2%3\n")
1535 .arg(_clock_bias, 19, 'e', 12)
1536 .arg(_clock_drift, 19, 'e', 12)
1537 .arg(_clock_driftrate, 19, 'e', 12);
1538
1539 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
1540 // =====================
1541 // BROADCAST ORBIT - 1
1542 // =====================
1543 out << QString(fmt)
1544 .arg(_IODnav, 19, 'e', 12)
1545 .arg(_Crs, 19, 'e', 12)
1546 .arg(_Delta_n, 19, 'e', 12)
1547 .arg(_M0, 19, 'e', 12);
1548 // =====================
1549 // BROADCAST ORBIT - 2
1550 // =====================
1551 out << QString(fmt)
1552 .arg(_Cuc, 19, 'e', 12)
1553 .arg(_e, 19, 'e', 12)
1554 .arg(_Cus, 19, 'e', 12)
1555 .arg(_sqrt_A, 19, 'e', 12);
1556 // =====================
1557 // BROADCAST ORBIT - 3
1558 // =====================
1559 out << QString(fmt)
1560 .arg(_TOEsec, 19, 'e', 12)
1561 .arg(_Cic, 19, 'e', 12)
1562 .arg(_OMEGA0, 19, 'e', 12)
1563 .arg(_Cis, 19, 'e', 12);
1564 // =====================
1565 // BROADCAST ORBIT - 4
1566 // =====================
1567 out << QString(fmt)
1568 .arg(_i0, 19, 'e', 12)
1569 .arg(_Crc, 19, 'e', 12)
1570 .arg(_omega, 19, 'e', 12)
1571 .arg(_OMEGADOT, 19, 'e', 12);
1572 // =====================
1573 // BROADCAST ORBIT - 5
1574 // =====================
1575 int dataSource = 0;
1576 int SVhealth = 0;
1577 double BGD_1_5A = _BGD_1_5A;
1578 double BGD_1_5B = _BGD_1_5B;
1579 if (_fnav) {
1580 dataSource |= (1<<1);
1581 dataSource |= (1<<8);
1582 BGD_1_5B = 0.0;
1583 // SVhealth
1584 // Bit 3 : E5a DVS
1585 if (_e5aDataInValid) {
1586 SVhealth |= (1<<3);
1587 }
1588 // Bit 4-5: E5a HS
1589 if (_E5aHS == 1.0) {
1590 SVhealth |= (1<<4);
1591 }
1592 else if (_E5aHS == 2.0) {
1593 SVhealth |= (1<<5);
1594 }
1595 else if (_E5aHS == 3.0) {
1596 SVhealth |= (1<<4);
1597 SVhealth |= (1<<5);
1598 }
1599 }
1600 else if(_inav) {
1601 // Bit 2 and 0 are set because from MT1046 the data source cannot be determined
1602 // and RNXv3.03 says both can be set if the navigation messages were merged
1603 dataSource |= (1<<0);
1604 dataSource |= (1<<2);
1605 dataSource |= (1<<9);
1606 // SVhealth
1607 // Bit 0 : E1-B DVS
1608 if (_e1DataInValid) {
1609 SVhealth |= (1<<0);
1610 }
1611 // Bit 1-2: E1-B HS
1612 if (_E1_bHS == 1.0) {
1613 SVhealth |= (1<<1);
1614 }
1615 else if (_E1_bHS == 2.0) {
1616 SVhealth |= (1<<2);
1617 }
1618 else if (_E1_bHS == 3.0) {
1619 SVhealth |= (1<<1);
1620 SVhealth |= (1<<2);
1621 }
1622 // Bit 3 : E5a DVS
1623 if (_e5aDataInValid) {
1624 SVhealth |= (1<<3);
1625 }
1626 // Bit 4-5: E5a HS
1627 if (_E5aHS == 1.0) {
1628 SVhealth |= (1<<4);
1629 }
1630 else if (_E5aHS == 2.0) {
1631 SVhealth |= (1<<5);
1632 }
1633 else if (_E5aHS == 3.0) {
1634 SVhealth |= (1<<4);
1635 SVhealth |= (1<<5);
1636 }
1637 // Bit 6 : E5b DVS
1638 if (_e5bDataInValid) {
1639 SVhealth |= (1<<6);
1640 }
1641 // Bit 7-8: E5b HS
1642 if (_E5bHS == 1.0) {
1643 SVhealth |= (1<<7);
1644 }
1645 else if (_E5bHS == 2.0) {
1646 SVhealth |= (1<<8);
1647 }
1648 else if (_E5bHS == 3.0) {
1649 SVhealth |= (1<<7);
1650 SVhealth |= (1<<8);
1651 }
1652 }
1653
1654 out << QString(fmt)
1655 .arg(_IDOT, 19, 'e', 12)
1656 .arg(double(dataSource), 19, 'e', 12)
1657 .arg(_TOEweek + 1024.0, 19, 'e', 12)
1658 .arg(0.0, 19, 'e', 12);
1659 // =====================
1660 // BROADCAST ORBIT - 6
1661 // =====================
1662 out << QString(fmt)
1663 .arg(_SISA, 19, 'e', 12)
1664 .arg(double(SVhealth), 19, 'e', 12)
1665 .arg(BGD_1_5A, 19, 'e', 12)
1666 .arg(BGD_1_5B, 19, 'e', 12);
1667 // =====================
1668 // BROADCAST ORBIT - 7
1669 // =====================
1670 double tot = _TOT;
1671 if (tot == 0.9999e9 && version < 3.0) {
1672 tot = 0.0;
1673 }
1674 out << QString(fmt)
1675 .arg(tot, 19, 'e', 12)
1676 .arg("", 19, QChar(' '))
1677 .arg("", 19, QChar(' '))
1678 .arg("", 19, QChar(' '));
1679
1680 return rnxStr;
1681}
1682
1683// Constructor
1684//////////////////////////////////////////////////////////////////////////////
1685t_ephSBAS::t_ephSBAS(double rnxVersion, const QStringList& lines) {
1686
1687 const int nLines = 4;
1688
1689 if (lines.size() != nLines) {
1690 _checkState = bad;
1691 return;
1692 }
1693
1694 // RINEX Format
1695 // ------------
1696 int fieldLen = 19;
1697
1698 int pos[4];
1699 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
1700 pos[1] = pos[0] + fieldLen;
1701 pos[2] = pos[1] + fieldLen;
1702 pos[3] = pos[2] + fieldLen;
1703
1704 // Read four lines
1705 // ---------------
1706 for (int iLine = 0; iLine < nLines; iLine++) {
1707 QString line = lines[iLine];
1708
1709 if ( iLine == 0 ) {
1710 QTextStream in(line.left(pos[1]).toLatin1());
1711
1712 int year, month, day, hour, min;
1713 double sec;
1714
1715 QString prnStr, n;
1716 in >> prnStr;
1717 if (prnStr.size() == 1 && prnStr[0] == 'S') {
1718 in >> n;
1719 prnStr.append(n);
1720 }
1721 in >> year >> month >> day >> hour >> min >> sec;
1722 if (prnStr.at(0) == 'S') {
1723 _prn.set('S', prnStr.mid(1).toInt());
1724 }
1725 else {
1726 _prn.set('S', prnStr.toInt());
1727 }
1728
1729 if (year < 80) {
1730 year += 2000;
1731 }
1732 else if (year < 100) {
1733 year += 1900;
1734 }
1735
1736 _TOC.set(year, month, day, hour, min, sec);
1737
1738 if ( readDbl(line, pos[1], fieldLen, _agf0 ) ||
1739 readDbl(line, pos[2], fieldLen, _agf1 ) ||
1740 readDbl(line, pos[3], fieldLen, _TOT ) ) {
1741 _checkState = bad;
1742 return;
1743 }
1744 }
1745 // =====================
1746 // BROADCAST ORBIT - 1
1747 // =====================
1748 else if ( iLine == 1 ) {
1749 if ( readDbl(line, pos[0], fieldLen, _x_pos ) ||
1750 readDbl(line, pos[1], fieldLen, _x_velocity ) ||
1751 readDbl(line, pos[2], fieldLen, _x_acceleration) ||
1752 readDbl(line, pos[3], fieldLen, _health ) ) {
1753 _checkState = bad;
1754 return;
1755 }
1756 }
1757 // =====================
1758 // BROADCAST ORBIT - 2
1759 // =====================
1760 else if ( iLine == 2 ) {
1761 if ( readDbl(line, pos[0], fieldLen, _y_pos ) ||
1762 readDbl(line, pos[1], fieldLen, _y_velocity ) ||
1763 readDbl(line, pos[2], fieldLen, _y_acceleration ) ||
1764 readDbl(line, pos[3], fieldLen, _ura ) ) {
1765 _checkState = bad;
1766 return;
1767 }
1768 }
1769 // =====================
1770 // BROADCAST ORBIT - 3
1771 // =====================
1772 else if ( iLine == 3 ) {
1773 double iodn;
1774 if ( readDbl(line, pos[0], fieldLen, _z_pos ) ||
1775 readDbl(line, pos[1], fieldLen, _z_velocity ) ||
1776 readDbl(line, pos[2], fieldLen, _z_acceleration) ||
1777 readDbl(line, pos[3], fieldLen, iodn ) ) {
1778 _checkState = bad;
1779 return;
1780 } else {
1781 _IODN = int(iodn);
1782 }
1783 }
1784 }
1785
1786 _x_pos *= 1.e3;
1787 _y_pos *= 1.e3;
1788 _z_pos *= 1.e3;
1789 _x_velocity *= 1.e3;
1790 _y_velocity *= 1.e3;
1791 _z_velocity *= 1.e3;
1792 _x_acceleration *= 1.e3;
1793 _y_acceleration *= 1.e3;
1794 _z_acceleration *= 1.e3;
1795}
1796
1797// IOD of SBAS Ephemeris (virtual)
1798////////////////////////////////////////////////////////////////////////////
1799
1800unsigned int t_ephSBAS::IOD() const {
1801 unsigned char buffer[80];
1802 int size = 0;
1803 int numbits = 0;
1804 long long bitbuffer = 0;
1805 unsigned char *startbuffer = buffer;
1806
1807 SBASADDBITSFLOAT(30, this->_x_pos, 0.08)
1808 SBASADDBITSFLOAT(30, this->_y_pos, 0.08)
1809 SBASADDBITSFLOAT(25, this->_z_pos, 0.4)
1810 SBASADDBITSFLOAT(17, this->_x_velocity, 0.000625)
1811 SBASADDBITSFLOAT(17, this->_y_velocity, 0.000625)
1812 SBASADDBITSFLOAT(18, this->_z_velocity, 0.004)
1813 SBASADDBITSFLOAT(10, this->_x_acceleration, 0.0000125)
1814 SBASADDBITSFLOAT(10, this->_y_acceleration, 0.0000125)
1815 SBASADDBITSFLOAT(10, this->_z_acceleration, 0.0000625)
1816 SBASADDBITSFLOAT(12, this->_agf0, 1.0/static_cast<double>(1<<30)/static_cast<double>(1<<1))
1817 SBASADDBITSFLOAT(8, this->_agf1, 1.0/static_cast<double>(1<<30)/static_cast<double>(1<<10))
1818 SBASADDBITS(5,0); // the last byte is filled by 0-bits to obtain a length of an integer multiple of 8
1819
1820 return CRC24(size, startbuffer);
1821}
1822
1823// Compute SBAS Satellite Position (virtual)
1824////////////////////////////////////////////////////////////////////////////
1825t_irc t_ephSBAS::position(int GPSweek, double GPSweeks, double* xc, double* vv) const {
1826
1827 bncTime tt(GPSweek, GPSweeks);
1828 double dt = tt - _TOC;
1829
1830 xc[0] = _x_pos + _x_velocity * dt + _x_acceleration * dt * dt / 2.0;
1831 xc[1] = _y_pos + _y_velocity * dt + _y_acceleration * dt * dt / 2.0;
1832 xc[2] = _z_pos + _z_velocity * dt + _z_acceleration * dt * dt / 2.0;
1833
1834 vv[0] = _x_velocity + _x_acceleration * dt;
1835 vv[1] = _y_velocity + _y_acceleration * dt;
1836 vv[2] = _z_velocity + _z_acceleration * dt;
1837
1838 xc[3] = _agf0 + _agf1 * dt;
1839
1840 xc[4] = _agf1;
1841 xc[5] = 0.0;
1842
1843 return success;
1844}
1845
1846// Health status of SBAS Ephemeris (virtual)
1847////////////////////////////////////////////////////////////////////////////
1848unsigned int t_ephSBAS::isUnhealthy() const {
1849
1850 // Bit 5
1851 bool URAindexIs15 = (int(_health) & (1<<5));
1852 if (URAindexIs15) {
1853 // in this case it is recommended
1854 // to set the bits 0,1,2,3 to 1 (MT17health = 15)
1855 return 1;
1856 }
1857
1858 // Bit 0-3
1859 int MT17health = (int(_health)) & (0x0f);
1860 if (MT17health) {
1861 return 1;
1862 }
1863
1864 // Bit 4
1865 // bool MT17HealthIsUnavailable = (int(_health) & (1<<4));
1866 // is not used because the MT17health bits can be independently set if URA index is 15
1867
1868 return 0;
1869}
1870
1871
1872// RINEX Format String
1873//////////////////////////////////////////////////////////////////////////////
1874QString t_ephSBAS::toString(double version) const {
1875
1876 QString navStr = navTypeString(_navType, _prn, version);
1877 QString rnxStr = navStr + rinexDateStr(_TOC, _prn, version);
1878
1879 QTextStream out(&rnxStr);
1880
1881 out << QString("%1%2%3\n")
1882 .arg(_agf0, 19, 'e', 12)
1883 .arg(_agf1, 19, 'e', 12)
1884 .arg(_TOT, 19, 'e', 12);
1885
1886 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
1887 // =====================
1888 // BROADCAST ORBIT - 1
1889 // =====================
1890 out << QString(fmt)
1891 .arg(1.e-3*_x_pos, 19, 'e', 12)
1892 .arg(1.e-3*_x_velocity, 19, 'e', 12)
1893 .arg(1.e-3*_x_acceleration, 19, 'e', 12)
1894 .arg(_health, 19, 'e', 12);
1895 // =====================
1896 // BROADCAST ORBIT - 2
1897 // =====================
1898 out << QString(fmt)
1899 .arg(1.e-3*_y_pos, 19, 'e', 12)
1900 .arg(1.e-3*_y_velocity, 19, 'e', 12)
1901 .arg(1.e-3*_y_acceleration, 19, 'e', 12)
1902 .arg(_ura, 19, 'e', 12);
1903 // =====================
1904 // BROADCAST ORBIT - 3
1905 // =====================
1906 out << QString(fmt)
1907 .arg(1.e-3*_z_pos, 19, 'e', 12)
1908 .arg(1.e-3*_z_velocity, 19, 'e', 12)
1909 .arg(1.e-3*_z_acceleration, 19, 'e', 12)
1910 .arg(double(_IODN), 19, 'e', 12);
1911
1912 return rnxStr;
1913}
1914
1915// Constructor
1916//////////////////////////////////////////////////////////////////////////////
1917t_ephBDS::t_ephBDS(double rnxVersion, const QStringList& lines) {
1918
1919 int nLines = 8;
1920
1921 if (navType() == t_eph::CNV1 ||
1922 navType() == t_eph::CNV2) {
1923 nLines += 2;
1924 }
1925 if (navType() == t_eph::CNV3) {
1926 nLines += 1;
1927 }
1928
1929 if (lines.size() != nLines) {
1930 _checkState = bad;
1931 return;
1932 }
1933
1934 // RINEX Format
1935 // ------------
1936 int fieldLen = 19;
1937
1938 int pos[4];
1939 pos[0] = (rnxVersion <= 2.12) ? 3 : 4;
1940 pos[1] = pos[0] + fieldLen;
1941 pos[2] = pos[1] + fieldLen;
1942 pos[3] = pos[2] + fieldLen;
1943
1944 // Read eight lines
1945 // ----------------
1946 for (int iLine = 0; iLine < nLines; iLine++) {
1947 QString line = lines[iLine];
1948
1949 if ( iLine == 0 ) {
1950 QTextStream in(line.left(pos[1]).toLatin1());
1951
1952 int year, month, day, hour, min;
1953 double sec;
1954
1955 QString prnStr, n;
1956 in >> prnStr;
1957 if (prnStr.size() == 1 && prnStr[0] == 'C') {
1958 in >> n;
1959 prnStr.append(n);
1960 }
1961 in >> year >> month >> day >> hour >> min >> sec;
1962 if (prnStr.at(0) == 'C') {
1963 _prn.set('C', prnStr.mid(1).toInt());
1964 }
1965 else {
1966 _prn.set('C', prnStr.toInt());
1967 }
1968
1969 if (year < 80) {
1970 year += 2000;
1971 }
1972 else if (year < 100) {
1973 year += 1900;
1974 }
1975
1976 _TOC.setBDS(year, month, day, hour, min, sec);
1977
1978 if ( readDbl(line, pos[1], fieldLen, _clock_bias ) ||
1979 readDbl(line, pos[2], fieldLen, _clock_drift ) ||
1980 readDbl(line, pos[3], fieldLen, _clock_driftrate) ) {
1981 _checkState = bad;
1982 return;
1983 }
1984 }
1985 // =====================
1986 // BROADCAST ORBIT - 1
1987 // =====================
1988 else if ( iLine == 1 ) {
1989 double aode;
1990 if ( readDbl(line, pos[0], fieldLen, aode ) ||
1991 readDbl(line, pos[1], fieldLen, _Crs ) ||
1992 readDbl(line, pos[2], fieldLen, _Delta_n) ||
1993 readDbl(line, pos[3], fieldLen, _M0 ) ) {
1994 _checkState = bad;
1995 return;
1996 }
1997 _AODE = int(aode);
1998 }
1999 // =====================
2000 // BROADCAST ORBIT - 2
2001 // =====================
2002 else if ( iLine == 2 ) {
2003 if ( readDbl(line, pos[0], fieldLen, _Cuc ) ||
2004 readDbl(line, pos[1], fieldLen, _e ) ||
2005 readDbl(line, pos[2], fieldLen, _Cus ) ||
2006 readDbl(line, pos[3], fieldLen, _sqrt_A) ) {
2007 _checkState = bad;
2008 return;
2009 }
2010 }
2011 // =====================
2012 // BROADCAST ORBIT - 3
2013 // =====================
2014 else if ( iLine == 3 ) {
2015 if ( readDbl(line, pos[0], fieldLen, _TOEsec ) ||
2016 readDbl(line, pos[1], fieldLen, _Cic ) ||
2017 readDbl(line, pos[2], fieldLen, _OMEGA0) ||
2018 readDbl(line, pos[3], fieldLen, _Cis ) ) {
2019 _checkState = bad;
2020 return;
2021 }
2022 }
2023 // =====================
2024 // BROADCAST ORBIT - 4
2025 // =====================
2026 else if ( iLine == 4 ) {
2027 if ( readDbl(line, pos[0], fieldLen, _i0 ) ||
2028 readDbl(line, pos[1], fieldLen, _Crc ) ||
2029 readDbl(line, pos[2], fieldLen, _omega ) ||
2030 readDbl(line, pos[3], fieldLen, _OMEGADOT) ) {
2031 _checkState = bad;
2032 return;
2033 }
2034 }
2035 // =====================
2036 // BROADCAST ORBIT - 5
2037 // =====================
2038 else if ( iLine == 5 ) {
2039
2040 if (navType() == t_eph::CNV1 ||
2041 navType() == t_eph::CNV2 ||
2042 navType() == t_eph::CNV3 ) {
2043 if ( readDbl(line, pos[0], fieldLen, _IDOT ) ||
2044 readDbl(line, pos[1], fieldLen, _Delta_n_dot) ||
2045 readDbl(line, pos[2], fieldLen, _satType ) ||
2046 readDbl(line, pos[3], fieldLen, _top ) ) {
2047 _checkState = bad;
2048 return;
2049 }
2050 }
2051 else { // D1, D2, undefined
2052 if ( readDbl(line, pos[0], fieldLen, _IDOT ) ||
2053 readDbl(line, pos[2], fieldLen, _BDTweek)) {
2054 _checkState = bad;
2055 return;
2056 }
2057 }
2058 }
2059 // =====================
2060 // BROADCAST ORBIT - 6
2061 // =====================
2062 else if ( iLine == 6 ) {
2063 if (navType() == t_eph::CNV1 ||
2064 navType() == t_eph::CNV2 ||
2065 navType() == t_eph::CNV3 ) {
2066 if ( readDbl(line, pos[0], fieldLen, _SISAI_oe ) ||
2067 readDbl(line, pos[1], fieldLen, _SISAI_ocb ) ||
2068 readDbl(line, pos[2], fieldLen, _SISAI_oc1 ) ||
2069 readDbl(line, pos[3], fieldLen, _SISAI_oc2 ) ) {
2070 _checkState = bad;
2071 return;
2072 }
2073 }
2074 else { // D1, D2, undefined
2075 double SatH1;
2076 if ( readDbl(line, pos[0], fieldLen, _URA ) ||
2077 readDbl(line, pos[1], fieldLen, SatH1) ||
2078 readDbl(line, pos[2], fieldLen, _TGD1) ||
2079 readDbl(line, pos[3], fieldLen, _TGD2) ) {
2080 _checkState = bad;
2081 return;
2082 }
2083 _SatH1 = int(SatH1);
2084 }
2085 }
2086 // =====================
2087 // BROADCAST ORBIT - 7
2088 // =====================
2089 else if ( iLine == 7 ) {
2090 if (navType() == t_eph::CNV1) {
2091 if ( readDbl(line, pos[0], fieldLen, _ISC_B1Cd) ||
2092 readDbl(line, pos[2], fieldLen, _TGD_B1Cp) ||
2093 readDbl(line, pos[3], fieldLen, _TGD_B2ap) ) {
2094 _checkState = bad;
2095 return;
2096 }
2097 }
2098 else if (navType() == t_eph::CNV2) {
2099 if ( readDbl(line, pos[1], fieldLen, _ISC_B2ad) ||
2100 readDbl(line, pos[2], fieldLen, _TGD_B1Cp) ||
2101 readDbl(line, pos[3], fieldLen, _TGD_B2ap) ) {
2102 _checkState = bad;
2103 return;
2104 }
2105 }
2106 else if (navType() == t_eph::CNV3) {
2107 double health;
2108 if ( readDbl(line, pos[0], fieldLen, _SISMAI ) ||
2109 readDbl(line, pos[1], fieldLen, health ) ||
2110 readDbl(line, pos[2], fieldLen, _INTEGRITYF_B2b) ||
2111 readDbl(line, pos[3], fieldLen, _TGD_B2bI) ) {
2112 _checkState = bad;
2113 return;
2114 }
2115 _health = int(health);
2116 }
2117 else { // D1, D2 or undefined
2118 double aodc;
2119 if ( readDbl(line, pos[0], fieldLen, _TOT) ||
2120 readDbl(line, pos[1], fieldLen, aodc) ) {
2121 _checkState = bad;
2122 return;
2123 }
2124 if (_TOT == 0.9999e9) { // 0.9999e9 means not known (RINEX standard)
2125 _TOT = _TOEsec;
2126 }
2127 _AODC = int(aodc);
2128 }
2129 }
2130 // =====================
2131 // BROADCAST ORBIT - 8
2132 // =====================
2133 else if ( iLine == 8 ) {
2134 double health;
2135 if (navType() == t_eph::CNV1) {
2136 if ( readDbl(line, pos[0], fieldLen, _SISMAI ) ||
2137 readDbl(line, pos[1], fieldLen, health ) ||
2138 readDbl(line, pos[2], fieldLen, _INTEGRITYF_B1C) ||
2139 readDbl(line, pos[3], fieldLen, _IODC) ) {
2140 _checkState = bad;
2141 return;
2142 }
2143 _health = int(health);
2144 }
2145 else if (navType() == t_eph::CNV2) {
2146 if ( readDbl(line, pos[0], fieldLen, _SISMAI ) ||
2147 readDbl(line, pos[1], fieldLen, health ) ||
2148 readDbl(line, pos[2], fieldLen, _INTEGRITYF_B2aB1C) ||
2149 readDbl(line, pos[3], fieldLen, _IODC) ) {
2150 _checkState = bad;
2151 return;
2152 }
2153 _health = int(health);
2154 }
2155 else if (navType() == t_eph::CNV3) {
2156 if ( readDbl(line, pos[0], fieldLen, _TOT)) {
2157 _checkState = bad;
2158 return;
2159 }
2160 }
2161
2162 }
2163 // =====================
2164 // BROADCAST ORBIT - 9
2165 // =====================
2166 else if ( iLine == 9 ) {
2167
2168 if (navType() == t_eph::CNV1 ||
2169 navType() == t_eph::CNV2) {
2170 if ( readDbl(line, pos[0], fieldLen, _TOT) ||
2171 readDbl(line, pos[3], fieldLen, _IODE) ) {
2172 _checkState = bad;
2173 return;
2174 }
2175 }
2176
2177 }
2178 }
2179
2180 _TOE.setBDS(int(_BDTweek), _TOEsec);
2181 // remark: actually should be computed from second_tot
2182 // but it seems to be unreliable in RINEX files
2183 //_TOT = _TOC.bdssec();
2184}
2185
2186// IOD of BDS Ephemeris (virtual)
2187////////////////////////////////////////////////////////////////////////////
2188unsigned int t_ephBDS::IOD() const {
2189 return (int(_TOC.gpssec())/720) % 240; //return (int(_TOEsec)/720) % 240;
2190}
2191
2192// Compute BDS Satellite Position (virtual)
2193//////////////////////////////////////////////////////////////////////////////
2194t_irc t_ephBDS::position(int GPSweek, double GPSweeks, double* xc, double* vv) const {
2195
2196 static const double gmBDS = 398.6004418e12;
2197 static const double omegaBDS = 7292115.0000e-11;
2198
2199 xc[0] = xc[1] = xc[2] = xc[3] = 0.0;
2200 vv[0] = vv[1] = vv[2] = 0.0;
2201
2202 bncTime tt(GPSweek, GPSweeks);
2203
2204 if (_sqrt_A == 0) {
2205 return failure;
2206 }
2207 double a0 = _sqrt_A * _sqrt_A;
2208
2209 double n0 = sqrt(gmBDS/(a0*a0*a0));
2210 double tk = tt - _TOE;
2211 double n = n0 + _Delta_n;
2212 double M = _M0 + n*tk;
2213 double E = M;
2214 double E_last;
2215 int nLoop = 0;
2216 do {
2217 E_last = E;
2218 E = M + _e*sin(E);
2219
2220 if (++nLoop == 100) {
2221 return failure;
2222 }
2223 } while ( fabs(E-E_last)*a0 > 0.001 );
2224
2225 double v = atan2(sqrt(1-_e*_e) * sin(E), cos(E) - _e);
2226 double u0 = v + _omega;
2227 double sin2u0 = sin(2*u0);
2228 double cos2u0 = cos(2*u0);
2229 double r = a0*(1 - _e*cos(E)) + _Crc*cos2u0 + _Crs*sin2u0;
2230 double i = _i0 + _IDOT*tk + _Cic*cos2u0 + _Cis*sin2u0;
2231 double u = u0 + _Cuc*cos2u0 + _Cus*sin2u0;
2232 double xp = r*cos(u);
2233 double yp = r*sin(u);
2234 double toesec = (_TOE.gpssec() - 14.0);
2235 double sinom = 0;
2236 double cosom = 0;
2237 double sini = 0;
2238 double cosi = 0;
2239
2240 // Velocity
2241 // --------
2242 double tanv2 = tan(v/2);
2243 double dEdM = 1 / (1 - _e*cos(E));
2244 double dotv = sqrt((1.0 + _e)/(1.0 - _e)) / cos(E/2)/cos(E/2)
2245 / (1 + tanv2*tanv2) * dEdM * n;
2246 double dotu = dotv + (-_Cuc*sin2u0 + _Cus*cos2u0)*2*dotv;
2247 double doti = _IDOT + (-_Cic*sin2u0 + _Cis*cos2u0)*2*dotv;
2248 double dotr = a0 * _e*sin(E) * dEdM * n
2249 + (-_Crc*sin2u0 + _Crs*cos2u0)*2*dotv;
2250
2251 double dotx = dotr*cos(u) - r*sin(u)*dotu;
2252 double doty = dotr*sin(u) + r*cos(u)*dotu;
2253
2254 const double iMaxGEO = 10.0 / 180.0 * M_PI;
2255
2256 // MEO/IGSO satellite
2257 // ------------------
2258 if (_i0 > iMaxGEO) {
2259 double OM = _OMEGA0 + (_OMEGADOT - omegaBDS)*tk - omegaBDS*toesec;
2260
2261 sinom = sin(OM);
2262 cosom = cos(OM);
2263 sini = sin(i);
2264 cosi = cos(i);
2265
2266 xc[0] = xp*cosom - yp*cosi*sinom;
2267 xc[1] = xp*sinom + yp*cosi*cosom;
2268 xc[2] = yp*sini;
2269
2270 // Velocity
2271 // --------
2272
2273 double dotom = _OMEGADOT - t_CST::omega;
2274
2275 vv[0] = cosom *dotx - cosi*sinom *doty // dX / dr
2276 - xp*sinom*dotom - yp*cosi*cosom*dotom // dX / dOMEGA
2277 + yp*sini*sinom*doti; // dX / di
2278
2279 vv[1] = sinom *dotx + cosi*cosom *doty
2280 + xp*cosom*dotom - yp*cosi*sinom*dotom
2281 - yp*sini*cosom*doti;
2282
2283 vv[2] = sini *doty + yp*cosi *doti;
2284
2285 }
2286
2287 // GEO satellite
2288 // -------------
2289 else {
2290 double OM = _OMEGA0 + _OMEGADOT*tk - omegaBDS*toesec;
2291 double ll = omegaBDS*tk;
2292
2293 sinom = sin(OM);
2294 cosom = cos(OM);
2295 sini = sin(i);
2296 cosi = cos(i);
2297
2298 double xx = xp*cosom - yp*cosi*sinom;
2299 double yy = xp*sinom + yp*cosi*cosom;
2300 double zz = yp*sini;
2301
2302 Matrix RX = BNC_PPP::t_astro::rotX(-5.0 / 180.0 * M_PI);
2303 Matrix RZ = BNC_PPP::t_astro::rotZ(ll);
2304
2305 ColumnVector X1(3); X1 << xx << yy << zz;
2306 ColumnVector X2 = RZ*RX*X1;
2307
2308 xc[0] = X2(1);
2309 xc[1] = X2(2);
2310 xc[2] = X2(3);
2311
2312 double dotom = _OMEGADOT;
2313
2314 double vx = cosom *dotx - cosi*sinom *doty
2315 - xp*sinom*dotom - yp*cosi*cosom*dotom
2316 + yp*sini*sinom*doti;
2317
2318 double vy = sinom *dotx + cosi*cosom *doty
2319 + xp*cosom*dotom - yp*cosi*sinom*dotom
2320 - yp*sini*cosom*doti;
2321
2322 double vz = sini *doty + yp*cosi *doti;
2323
2324 ColumnVector V(3); V << vx << vy << vz;
2325
2326 Matrix RdotZ(3,3);
2327 double C = cos(ll);
2328 double S = sin(ll);
2329 Matrix UU(3,3);
2330 UU[0][0] = -S; UU[0][1] = +C; UU[0][2] = 0.0;
2331 UU[1][0] = -C; UU[1][1] = -S; UU[1][2] = 0.0;
2332 UU[2][0] = 0.0; UU[2][1] = 0.0; UU[2][2] = 0.0;
2333 RdotZ = omegaBDS * UU;
2334
2335 ColumnVector VV(3);
2336 VV = RZ*RX*V + RdotZ*RX*X1;
2337
2338 vv[0] = VV(1);
2339 vv[1] = VV(2);
2340 vv[2] = VV(3);
2341 }
2342
2343 double tc = tt - _TOC;
2344 xc[3] = _clock_bias + _clock_drift*tc + _clock_driftrate*tc*tc;
2345
2346// dotC = _clock_drift + _clock_driftrate*tc
2347// - 4.442807309e-10*_e * sqrt(a0) * cos(E) * dEdM * n;
2348
2349 // Relativistic Correction
2350 // -----------------------
2351 xc[3] -= 4.442807309e-10 * _e * sqrt(a0) *sin(E);
2352
2353 xc[4] = _clock_drift + _clock_driftrate*tc;
2354 xc[5] = _clock_driftrate;
2355
2356 return success;
2357}
2358
2359
2360// Health status of SBAS Ephemeris (virtual)
2361////////////////////////////////////////////////////////////////////////////
2362unsigned int t_ephBDS::isUnhealthy() const {
2363
2364 if (navType() == t_eph::CNV1 ||
2365 navType() == t_eph::CNV2 ||
2366 navType() == t_eph::CNV3) {
2367 return static_cast<unsigned int>(_health);
2368 }
2369
2370 return static_cast<unsigned int>(_SatH1);
2371
2372}
2373
2374
2375// RINEX Format String
2376//////////////////////////////////////////////////////////////////////////////
2377QString t_ephBDS::toString(double version) const {
2378
2379 QString navStr = navTypeString(_navType, _prn, version);
2380 QString rnxStr = navStr + rinexDateStr(_TOC-14.0, _prn, version);
2381
2382 QTextStream out(&rnxStr);
2383
2384 out << QString("%1%2%3\n")
2385 .arg(_clock_bias, 19, 'e', 12)
2386 .arg(_clock_drift, 19, 'e', 12)
2387 .arg(_clock_driftrate, 19, 'e', 12);
2388
2389 QString fmt = version < 3.0 ? " %1%2%3%4\n" : " %1%2%3%4\n";
2390 // =====================
2391 // BROADCAST ORBIT - 1
2392 // =====================
2393 out << QString(fmt)
2394 .arg(double(_AODE), 19, 'e', 12)
2395 .arg(_Crs, 19, 'e', 12)
2396 .arg(_Delta_n, 19, 'e', 12)
2397 .arg(_M0, 19, 'e', 12);
2398 // =====================
2399 // BROADCAST ORBIT - 2
2400 // =====================
2401 out << QString(fmt)
2402 .arg(_Cuc, 19, 'e', 12)
2403 .arg(_e, 19, 'e', 12)
2404 .arg(_Cus, 19, 'e', 12)
2405 .arg(_sqrt_A, 19, 'e', 12);
2406 // =====================
2407 // BROADCAST ORBIT - 3
2408 // =====================
2409 out << QString(fmt)
2410 .arg(_TOEsec, 19, 'e', 12)
2411 .arg(_Cic, 19, 'e', 12)
2412 .arg(_OMEGA0, 19, 'e', 12)
2413 .arg(_Cis, 19, 'e', 12);
2414 // =====================
2415 // BROADCAST ORBIT - 4
2416 // =====================
2417 out << QString(fmt)
2418 .arg(_i0, 19, 'e', 12)
2419 .arg(_Crc, 19, 'e', 12)
2420 .arg(_omega, 19, 'e', 12)
2421 .arg(_OMEGADOT, 19, 'e', 12);
2422 // =====================
2423 // BROADCAST ORBIT - 5
2424 // =====================
2425 if (navType() == t_eph::CNV1 ||
2426 navType() == t_eph::CNV2 ||
2427 navType() == t_eph::CNV3 ) {
2428 out << QString(fmt)
2429 .arg(_IDOT, 19, 'e', 12)
2430 .arg(_Delta_n_dot, 19, 'e', 12)
2431 .arg(_satType, 19, 'e', 12)
2432 .arg(_top, 19, 'e', 12);
2433 }
2434 else { // D1, D2, undefined
2435 out << QString(fmt)
2436 .arg(_IDOT, 19, 'e', 12)
2437 .arg("", 19, QChar(' '))
2438 .arg(_BDTweek, 19, 'e', 12)
2439 .arg("", 19, QChar(' '));
2440 }
2441 // =====================
2442 // BROADCAST ORBIT - 6
2443 // =====================
2444 if (navType() == t_eph::CNV1 ||
2445 navType() == t_eph::CNV2 ||
2446 navType() == t_eph::CNV3 ) {
2447 out << QString(fmt)
2448 .arg(_SISAI_oe, 19, 'e', 12)
2449 .arg(_SISAI_ocb, 19, 'e', 12)
2450 .arg(_SISAI_oc1, 19, 'e', 12)
2451 .arg(_SISAI_oc2, 19, 'e', 12);
2452 }
2453 else { // D1, D2, undefined
2454 out << QString(fmt)
2455 .arg(_URA, 19, 'e', 12)
2456 .arg(double(_SatH1), 19, 'e', 12)
2457 .arg(_TGD1, 19, 'e', 12)
2458 .arg(_TGD2, 19, 'e', 12);
2459 }
2460 // =====================
2461 // BROADCAST ORBIT - 7
2462 // =====================
2463 if (navType() == t_eph::CNV1) {
2464 out << QString(fmt)
2465 .arg(_ISC_B1Cd, 19, 'e', 12)
2466 .arg("", 19, QChar(' '))
2467 .arg(_TGD_B1Cp, 19, 'e', 12)
2468 .arg(_TGD_B2ap, 19, 'e', 12);
2469 }
2470 else if (navType() == t_eph::CNV2) {
2471 out << QString(fmt)
2472 .arg("", 19, QChar(' '))
2473 .arg(_ISC_B2ad, 19, 'e', 12)
2474 .arg(_TGD_B1Cp, 19, 'e', 12)
2475 .arg(_TGD_B2ap, 19, 'e', 12);
2476 }
2477 else if (navType() == t_eph::CNV3) {
2478 out << QString(fmt)
2479 .arg(_SISMAI, 19, 'e', 12)
2480 .arg(double(_health), 19, 'e', 12)
2481 .arg(_INTEGRITYF_B2b, 19, 'e', 12)
2482 .arg(_TGD_B2bI, 19, 'e', 12);
2483 }
2484 else { // D1, D2, undefined
2485 double tots = 0.0;
2486 if (_receptDateTime.isValid()) {// RTCM stream input
2487 tots = _TOE.bdssec();
2488 }
2489 else { // RINEX input
2490 tots = _TOT;
2491 }
2492 out << QString(fmt)
2493 .arg(tots, 19, 'e', 12)
2494 .arg(double(_AODC), 19, 'e', 12)
2495 .arg("", 19, QChar(' '))
2496 .arg("", 19, QChar(' '));
2497 }
2498
2499 // =====================
2500 // BROADCAST ORBIT - 8
2501 // =====================
2502 if (navType() == t_eph::CNV1) {
2503 out << QString(fmt)
2504 .arg(_SISMAI, 19, 'e', 12)
2505 .arg(double(_health), 19, 'e', 12)
2506 .arg(_INTEGRITYF_B1C, 19, 'e', 12)
2507 .arg(_IODC, 19, 'e', 12);
2508 }
2509 else if (navType() == t_eph::CNV2) {
2510 out << QString(fmt)
2511 .arg(_SISMAI, 19, 'e', 12)
2512 .arg(double(_health), 19, 'e', 12)
2513 .arg(_INTEGRITYF_B2aB1C, 19, 'e', 12)
2514 .arg(_IODC, 19, 'e', 12);
2515 }
2516 else if (navType() == t_eph::CNV3) {
2517 out << QString(fmt)
2518 .arg(_TOT, 19, 'e', 12)
2519 .arg("", 19, QChar(' '))
2520 .arg("", 19, QChar(' '))
2521 .arg("", 19, QChar(' '));
2522 }
2523
2524 // =====================
2525 // BROADCAST ORBIT - 9
2526 // =====================
2527 if (navType() == t_eph::CNV1 ||
2528 navType() == t_eph::CNV2) {
2529 out << QString(fmt)
2530 .arg(_TOT, 19, 'e', 12)
2531 .arg("", 19, QChar(' '))
2532 .arg("", 19, QChar(' '))
2533 .arg(_IODE, 19, 'e', 12);
2534 }
2535
2536
2537 return rnxStr;
2538}
Note: See TracBrowser for help on using the repository browser.