source: ntrip/trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp@ 11023

Last change on this file since 11023 was 11011, checked in by stuerze, 2 weeks ago

updates regarding RTCM-SSR

File size: 36.8 KB
Line 
1// Part of BNC, a utility for retrieving decoding and
2// converting GNSS data streams from NTRIP broadcasters.
3//
4// Copyright (C) 2007
5// German Federal Agency for Cartography and Geodesy (BKG)
6// http://www.bkg.bund.de
7// Czech Technical University Prague, Department of Geodesy
8// http://www.fsv.cvut.cz
9//
10// Email: euref-ip@bkg.bund.de
11//
12// This program is free software; you can redistribute it and/or
13// modify it under the terms of the GNU General Public License
14// as published by the Free Software Foundation, version 2.
15//
16// This program is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with this program; if not, write to the Free Software
23// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25/* -------------------------------------------------------------------------
26 * BKG NTRIP Client
27 * -------------------------------------------------------------------------
28 *
29 * Class: RTCM3coDecoder
30 *
31 * Purpose: RTCM3 Clock Orbit Decoder
32 *
33 * Author: L. Mervart
34 *
35 * Created: 05-May-2008
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <stdio.h>
42#include <math.h>
43#include <climits>
44
45#include "RTCM3coDecoder.h"
46#include "bncutils.h"
47#include "bncrinex.h"
48#include "bnccore.h"
49#include "bncsettings.h"
50#include "bnctime.h"
51
52using namespace std;
53
54// Constructor
55////////////////////////////////////////////////////////////////////////////
56RTCM3coDecoder::RTCM3coDecoder(const QString& staID) {
57
58 _staID = staID;
59
60 // File Output
61 // -----------
62 bncSettings settings;
63 QString path = settings.value("corrPath").toString();
64 if (!path.isEmpty()) {
65 expandEnvVar(path);
66 if ( path.length() > 0 && path[path.length()-1] != QDir::separator() ) {
67 path += QDir::separator();
68 }
69 _fileNameSkl = path + staID;
70 }
71 _out = 0;
72
73 connect(this, SIGNAL(newOrbCorrections(QList<t_orbCorr>)),
74 BNC_CORE, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)));
75
76 connect(this, SIGNAL(newClkCorrections(QList<t_clkCorr>)),
77 BNC_CORE, SLOT(slotNewClkCorrections(QList<t_clkCorr>)));
78
79 connect(this, SIGNAL(newCodeBiases(QList<t_satCodeBias>)),
80 BNC_CORE, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)));
81
82 connect(this, SIGNAL(newPhaseBiases(QList<t_satPhaseBias>)),
83 BNC_CORE, SLOT(slotNewPhaseBiases(QList<t_satPhaseBias>)));
84
85 connect(this, SIGNAL(newTec(t_vTec)),
86 BNC_CORE, SLOT(slotNewTec(t_vTec)));
87
88 connect(this, SIGNAL(newMetaData(t_metaData)),
89 BNC_CORE, SLOT(slotNewMetaData(t_metaData)));
90
91 connect(this, SIGNAL(providerIDChanged(QString)),
92 BNC_CORE, SIGNAL(providerIDChanged(QString)));
93
94 connect(this, SIGNAL(newMessage(QByteArray,bool)),
95 BNC_CORE, SLOT(slotMessage(const QByteArray,bool)));
96
97 reset();
98
99 _providerID[0] = -1;
100 _providerID[1] = -1;
101 _providerID[2] = -1;
102
103 _ssrCorr = 0;
104
105}
106
107// Destructor
108////////////////////////////////////////////////////////////////////////////
109RTCM3coDecoder::~RTCM3coDecoder() {
110 delete _out;
111 delete _ssrCorr;
112 _IODs.clear();
113 _orbCorrections.clear();
114 _clkCorrections.clear();
115 _lastClkCorrections.clear();
116 _codeBiases.clear();
117 _phaseBiases.clear();
118 _vTecMap.clear();
119 _metaDataMap.clear();
120}
121
122//
123////////////////////////////////////////////////////////////////////////////
124void RTCM3coDecoder::reset(bool resetPhaseBias) {
125 memset(&_clkOrb, 0, sizeof(_clkOrb));
126 memset(&_codeBias, 0, sizeof(_codeBias));
127 if (resetPhaseBias) {
128 memset(&_phaseBias, 0, sizeof(_phaseBias));
129 for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
130 _phaseBiasSentEpoch[s] = UINT_MAX; // "never sent"
131 }
132 }
133 memset(&_vTEC, 0, sizeof(_vTEC));
134}
135
136// Reopen Output File
137////////////////////////////////////////////////////////////////////////
138void RTCM3coDecoder::reopen() {
139
140 if (!_fileNameSkl.isEmpty()) {
141
142 bncSettings settings;
143
144 QDateTime datTim = currentDateAndTimeGPS();
145
146 QString hlpStr = bncRinex::nextEpochStr(datTim,
147 settings.value("corrIntr").toString(), 3);
148
149 QString cntStr = (_fileNameSkl.contains("ION")) ? "_ION.ssr" : "_MC.ssr";
150
151 QString fileNameHlp = _fileNameSkl +
152 "_S_" + // stream
153 QString("%1").arg(datTim.date().year()) +
154 QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
155 hlpStr + // HM_period
156 cntStr; // mixed ION or CLK
157
158 if (_fileName == fileNameHlp) {
159 return;
160 }
161 else {
162 _fileName = fileNameHlp;
163 }
164
165 delete _out;
166 if ( Qt::CheckState(settings.value("rnxAppend").toInt()) == Qt::Checked) {
167 _out = new ofstream( _fileName.toLatin1().data(), ios_base::out | ios_base::app );
168 }
169 else {
170 _out = new ofstream( _fileName.toLatin1().data() );
171 }
172 }
173}
174
175//
176////////////////////////////////////////////////////////////////////////////
177t_irc RTCM3coDecoder::Decode(char* buffer, int bufLen, vector<string>& errmsg) {
178
179 errmsg.clear();
180
181 _buffer.append(QByteArray(buffer,bufLen));
182
183 t_irc retCode = failure;
184
185 while(_buffer.size()) {
186
187 struct SsrCorr::ClockOrbit clkOrbSav;
188 struct SsrCorr::CodeBias codeBiasSav;
189 struct SsrCorr::PhaseBias phaseBiasSav;
190 struct SsrCorr::VTEC vTECSav;
191 struct SsrCorr::MetaData metaDataSav;
192 // save state
193 memcpy(&clkOrbSav, &_clkOrb, sizeof(clkOrbSav));
194 memcpy(&codeBiasSav, &_codeBias, sizeof(codeBiasSav));
195 memcpy(&phaseBiasSav, &_phaseBias, sizeof(phaseBiasSav));
196 memcpy(&vTECSav, &_vTEC, sizeof(vTECSav));
197 memcpy(&metaDataSav, &_metaData, sizeof(metaDataSav));
198
199 int bytesused = 0;
200
201 GCOB_RETURN irc = _ssrCorr->GetSSR(&_clkOrb, &_codeBias, &_vTEC, &_phaseBias, &_metaData,
202 _buffer.data(), _buffer.size(), &bytesused);
203
204 if (irc <= -30) { // not enough data - restore state and exit loop
205 memcpy(&_clkOrb, &clkOrbSav, sizeof(clkOrbSav));
206 memcpy(&_codeBias, &codeBiasSav, sizeof(codeBiasSav));
207 memcpy(&_phaseBias, &phaseBiasSav, sizeof(phaseBiasSav));
208 memcpy(&_vTEC, &vTECSav, sizeof(vTECSav));
209 memcpy(&_metaData, &metaDataSav, sizeof(metaDataSav));
210 break;
211 }
212
213 else if (irc < 0) { // error - skip this message (or 1 byte if the
214 // frame itself couldn't be recognized) and retry
215 if (irc != GCOBR_UNKNOWNTYPE) {
216 // A recognized-but-unimplemented message (e.g. Tropospheric or
217 // Regional Ionospheric Correction) leaves _clkOrb/_codeBias/
218 // _phaseBias/_vTEC untouched, and bytesused is already reliably set
219 // from its CRC-validated frame length, so there is nothing to
220 // discard. Every other error code may have left inconsistent
221 // partial state (e.g. mid-satellite-loop) or an unreliable frame
222 // boundary, so reset defensively there. Without this, streams with
223 // frequent unimplemented messages (e.g. dense grid-based Regional
224 // Ionospheric traffic) would wipe unrelated, still-valid orbit/
225 // clock/bias/phase-bias state every time one is skipped.
226 reset();
227 }
228 _buffer = _buffer.mid(bytesused ? bytesused : 1);
229 }
230
231 else { // OK or MESSAGEFOLLOWS
232 _buffer = _buffer.mid(bytesused);
233
234 if (irc == GCOBR_OK || irc == GCOBR_MESSAGEFOLLOWS ) {
235 setEpochTime(); // sets _lastTime
236
237 if (_lastTime.valid()) {
238 reopen();
239 checkProviderID();
240 sendResults();
241 retCode = success;
242 }
243 else {
244 emit newMessage("RTCM3coDecoder: _lastTime invalid: " + _staID.toLatin1(), true);
245 retCode = failure;
246 }
247 // Keep _phaseBias intact: an Extended Phase Bias message (COBOFS_PBEXT)
248 // arriving later needs to correlate against the satellite/signal list
249 // just published by this Phase Bias message. clock_orbit_rtcm_new.cpp's
250 // COBOFS_PBIAS handling resets pb->NumberOfSat[s] itself before decoding
251 // a fresh message, so this doesn't cause stale data to accumulate.
252 reset(false);
253 }
254 }
255 }
256
257 return retCode;
258}
259
260//
261////////////////////////////////////////////////////////////////////////////
262void RTCM3coDecoder::sendResults() {
263
264 // Orbit and clock corrections of all satellites
265 // ---------------------------------------------
266 for (unsigned ii = 0; ii < CLOCKORBIT_NUMGPS
267 + CLOCKORBIT_NUMGLONASS
268 + CLOCKORBIT_NUMGALILEO
269 + CLOCKORBIT_NUMQZSS
270 + CLOCKORBIT_NUMSBAS
271 + _clkOrb.NumberOfSat[CLOCKORBIT_SATBDS];
272 ii++) {
273 if (corrIsOutOfRange(_clkOrb.Sat[ii])) {
274 continue;
275 }
276 char sys = ' ';
277 int num = _clkOrb.Sat[ii].ID;
278 int flag = 0; // to force NAV type usage according SSR standard
279 if (ii < _clkOrb.NumberOfSat[CLOCKORBIT_SATGPS]) {
280 sys = 'G';
281 flag = t_eph::LNAV;
282 }
283 else if (ii >= CLOCKORBIT_OFFSETGLONASS &&
284 ii < CLOCKORBIT_OFFSETGLONASS + _clkOrb.NumberOfSat[CLOCKORBIT_SATGLONASS]) {
285 sys = 'R';
286 flag = t_eph::FDMA_M;
287 }
288 else if (ii >= CLOCKORBIT_OFFSETGALILEO &&
289 ii < CLOCKORBIT_OFFSETGALILEO + _clkOrb.NumberOfSat[CLOCKORBIT_SATGALILEO]) {
290 sys = 'E';
291 flag = t_eph::INAV;
292 }
293 else if (ii >= CLOCKORBIT_OFFSETQZSS &&
294 ii < CLOCKORBIT_OFFSETQZSS + _clkOrb.NumberOfSat[CLOCKORBIT_SATQZSS]) {
295 sys = 'J';
296 flag = t_eph::LNAV;
297 }
298 else if (ii >= CLOCKORBIT_OFFSETSBAS &&
299 ii < CLOCKORBIT_OFFSETSBAS + _clkOrb.NumberOfSat[CLOCKORBIT_SATSBAS]) {
300 sys = 'S';
301 flag = t_eph::SBASL1;
302 }
303 else if (ii >= CLOCKORBIT_OFFSETBDS &&
304 ii < CLOCKORBIT_OFFSETBDS + _clkOrb.NumberOfSat[CLOCKORBIT_SATBDS]) {
305 sys = 'C';
306 if (num < 6) {// GEO
307 flag = t_eph::D2;
308 }
309 else if (num > 58 && num < 63) { // GEO
310 flag = t_eph::D2;
311 }
312 else {
313 flag = t_eph::D1;
314 }
315 }
316 else {
317 continue;
318 }
319
320 // Orbit correction
321 // ----------------
322 if ( _clkOrb.messageType == _ssrCorr->COTYPE_GPSCOMBINED ||
323 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSCOMBINED ||
324 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOCOMBINED ||
325 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSCOMBINED ||
326 _clkOrb.messageType == _ssrCorr->COTYPE_SBASCOMBINED ||
327 _clkOrb.messageType == _ssrCorr->COTYPE_BDSCOMBINED ||
328 _clkOrb.messageType == _ssrCorr->COTYPE_GPSORBIT ||
329 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSORBIT ||
330 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOORBIT ||
331 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSORBIT ||
332 _clkOrb.messageType == _ssrCorr->COTYPE_SBASORBIT ||
333 _clkOrb.messageType == _ssrCorr->COTYPE_BDSORBIT ) {
334
335 t_orbCorr orbCorr;
336 orbCorr._prn.set(sys, num, flag);
337 orbCorr._staID = _staID.toStdString();
338 orbCorr._iod = _clkOrb.Sat[ii].IOD;
339 orbCorr._time = _lastTime;
340 orbCorr._updateInt = _clkOrb.UpdateInterval;
341 orbCorr._system = sys;
342 orbCorr._xr[0] = _clkOrb.Sat[ii].Orbit.DeltaRadial;
343 orbCorr._xr[1] = _clkOrb.Sat[ii].Orbit.DeltaAlongTrack;
344 orbCorr._xr[2] = _clkOrb.Sat[ii].Orbit.DeltaCrossTrack;
345 orbCorr._dotXr[0] = _clkOrb.Sat[ii].Orbit.DotDeltaRadial;
346 orbCorr._dotXr[1] = _clkOrb.Sat[ii].Orbit.DotDeltaAlongTrack;
347 orbCorr._dotXr[2] = _clkOrb.Sat[ii].Orbit.DotDeltaCrossTrack;
348 orbCorr._ssrFormat = (_type == RTCMssr) ? ssrRtcmOld :
349 (_type == RTCMnewssr) ? ssrRtcmNew : ssrUnknown;
350
351 _orbCorrections[_lastTime].append(orbCorr);
352
353 _IODs[orbCorr._prn] = _clkOrb.Sat[ii].IOD;
354 }
355
356 // Clock Corrections
357 // -----------------
358 if ( _clkOrb.messageType == _ssrCorr->COTYPE_GPSCOMBINED ||
359 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSCOMBINED ||
360 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOCOMBINED ||
361 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSCOMBINED ||
362 _clkOrb.messageType == _ssrCorr->COTYPE_SBASCOMBINED ||
363 _clkOrb.messageType == _ssrCorr->COTYPE_BDSCOMBINED ||
364 _clkOrb.messageType == _ssrCorr->COTYPE_GPSCLOCK ||
365 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSCLOCK ||
366 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOCLOCK ||
367 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSCLOCK ||
368 _clkOrb.messageType == _ssrCorr->COTYPE_SBASCLOCK ||
369 _clkOrb.messageType == _ssrCorr->COTYPE_BDSCLOCK) {
370
371 t_clkCorr clkCorr;
372 clkCorr._prn.set(sys, _clkOrb.Sat[ii].ID, flag);
373 clkCorr._staID = _staID.toStdString();
374 clkCorr._time = _lastTime;
375 clkCorr._updateInt = _clkOrb.UpdateInterval;
376 clkCorr._dClk = _clkOrb.Sat[ii].Clock.DeltaA0 / t_CST::c;
377 clkCorr._dotDClk = _clkOrb.Sat[ii].Clock.DeltaA1 / t_CST::c;
378 clkCorr._dotDotDClk = _clkOrb.Sat[ii].Clock.DeltaA2 / t_CST::c;
379
380 _lastClkCorrections[clkCorr._prn] = clkCorr;
381
382 if (_IODs.contains(clkCorr._prn)) {
383 clkCorr._iod = _IODs[clkCorr._prn];
384 _clkCorrections[_lastTime].append(clkCorr);
385 }
386 }
387
388 // High-Resolution Clocks
389 // ----------------------
390 if ( _clkOrb.messageType == _ssrCorr->COTYPE_GPSHR ||
391 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSHR ||
392 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOHR ||
393 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSHR ||
394 _clkOrb.messageType == _ssrCorr->COTYPE_SBASHR ||
395 _clkOrb.messageType == _ssrCorr->COTYPE_BDSHR) {
396 t_prn prn(sys, _clkOrb.Sat[ii].ID, flag);
397 if (_lastClkCorrections.contains(prn)) {
398 t_clkCorr clkCorr;
399 clkCorr = _lastClkCorrections[prn];
400 clkCorr._time = _lastTime;
401 clkCorr._updateInt = _clkOrb.UpdateInterval;
402 clkCorr._dClk += _clkOrb.Sat[ii].hrclock / t_CST::c;
403 if (_IODs.contains(clkCorr._prn)) {
404 clkCorr._iod = _IODs[clkCorr._prn];
405 _clkCorrections[_lastTime].append(clkCorr);
406 }
407 }
408 }
409 }
410
411 // Code Biases
412 // -----------
413 for (unsigned ii = 0; ii < CLOCKORBIT_NUMGPS
414 + CLOCKORBIT_NUMGLONASS
415 + CLOCKORBIT_NUMGALILEO
416 + CLOCKORBIT_NUMQZSS
417 + CLOCKORBIT_NUMSBAS
418 + _codeBias.NumberOfSat[CLOCKORBIT_SATBDS];
419 ii++) {
420 char sys = ' ';
421 int num = _codeBias.Sat[ii].ID;
422 int flag = 0;
423 if (ii < _codeBias.NumberOfSat[CLOCKORBIT_SATGPS]) {
424 sys = 'G';
425 flag = t_eph::LNAV;
426 }
427 else if (ii >= CLOCKORBIT_OFFSETGLONASS &&
428 ii < CLOCKORBIT_OFFSETGLONASS + _codeBias.NumberOfSat[CLOCKORBIT_SATGLONASS]) {
429 sys = 'R';
430 flag = t_eph::FDMA_M;
431 }
432 else if (ii >= CLOCKORBIT_OFFSETGALILEO &&
433 ii < CLOCKORBIT_OFFSETGALILEO + _codeBias.NumberOfSat[CLOCKORBIT_SATGALILEO]) {
434 sys = 'E';
435 flag = t_eph::INAV;
436 }
437 else if (ii >= CLOCKORBIT_OFFSETQZSS &&
438 ii < CLOCKORBIT_OFFSETQZSS + _codeBias.NumberOfSat[CLOCKORBIT_SATQZSS]) {
439 sys = 'J';
440 flag = t_eph::LNAV;
441 }
442 else if (ii >= CLOCKORBIT_OFFSETSBAS &&
443 ii < CLOCKORBIT_OFFSETSBAS + _codeBias.NumberOfSat[CLOCKORBIT_SATSBAS]) {
444 sys = 'S';
445 flag = t_eph::SBASL1;
446 }
447 else if (ii >= CLOCKORBIT_OFFSETBDS &&
448 ii < CLOCKORBIT_OFFSETBDS + _codeBias.NumberOfSat[CLOCKORBIT_SATBDS]) {
449 sys = 'C';
450 if (num < 6) {// GEO
451 flag = t_eph::D2;
452 }
453 else if (num > 58 && num < 63) { // GEO
454 flag = t_eph::D2;
455 }
456 else {
457 flag = t_eph::D1;
458 }
459 }
460 else {
461 continue;
462 }
463 t_satCodeBias satCodeBias;
464 satCodeBias._prn.set(sys, num, flag);
465 satCodeBias._staID = _staID.toStdString();
466 satCodeBias._time = _lastTime;
467 satCodeBias._updateInt = _codeBias.UpdateInterval;
468 for (unsigned jj = 0; jj < _codeBias.Sat[ii].NumberOfCodeBiases; jj++) {
469 const SsrCorr::CodeBias::BiasSat::CodeBiasEntry& biasEntry = _codeBias.Sat[ii].Biases[jj];
470 t_frqCodeBias frqCodeBias;
471 frqCodeBias._rnxType2ch.assign(_ssrCorr->codeTypeToRnxType(sys, biasEntry.Type));
472 frqCodeBias._value = biasEntry.Bias;
473 if (!frqCodeBias._rnxType2ch.empty()) {
474 satCodeBias._bias.push_back(frqCodeBias);
475 }
476 }
477 _codeBiases[_lastTime].append(satCodeBias);
478 }
479
480 // Phase Biases
481 // -----------
482 // _phaseBias is no longer wiped between messages (kept alive so a later
483 // Extended Phase Bias message can correlate against it - see
484 // COBOFS_PBEXT), so without this guard the block below would re-append
485 // the same system's satellites into the output on every other message
486 // decoded in between two actual phase-bias messages. Only emit a
487 // system's satellites once per epoch, tracked per system by comparing
488 // against the epoch we last actually sent for it.
489 bool phaseBiasFresh[CLOCKORBIT_SATNUM];
490 for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
491 phaseBiasFresh[s] = _phaseBias.NumberOfSat[s] > 0 &&
492 _phaseBias.EpochTime[s] != _phaseBiasSentEpoch[s];
493 if (phaseBiasFresh[s]) {
494 _phaseBiasSentEpoch[s] = _phaseBias.EpochTime[s];
495 }
496 }
497 for (unsigned ii = 0; ii < CLOCKORBIT_NUMGPS
498 + CLOCKORBIT_NUMGLONASS
499 + CLOCKORBIT_NUMGALILEO
500 + CLOCKORBIT_NUMQZSS
501 + CLOCKORBIT_NUMSBAS
502 + _phaseBias.NumberOfSat[CLOCKORBIT_SATBDS];
503 ii++) {
504 char sys = ' ';
505 int num = _phaseBias.Sat[ii].ID;
506 int flag = 0;
507 if (ii < _phaseBias.NumberOfSat[CLOCKORBIT_SATGPS]) {
508 if (!phaseBiasFresh[CLOCKORBIT_SATGPS]) continue;
509 sys = 'G';
510 flag = t_eph::LNAV;
511 }
512 else if (ii >= CLOCKORBIT_OFFSETGLONASS &&
513 ii < CLOCKORBIT_OFFSETGLONASS + _phaseBias.NumberOfSat[CLOCKORBIT_SATGLONASS]) {
514 if (!phaseBiasFresh[CLOCKORBIT_SATGLONASS]) continue;
515 sys = 'R';
516 flag = t_eph::FDMA_M;
517 }
518 else if (ii >= CLOCKORBIT_OFFSETGALILEO &&
519 ii < CLOCKORBIT_OFFSETGALILEO + _phaseBias.NumberOfSat[CLOCKORBIT_SATGALILEO]) {
520 if (!phaseBiasFresh[CLOCKORBIT_SATGALILEO]) continue;
521 sys = 'E';
522 flag = t_eph::INAV;
523 }
524 else if (ii >= CLOCKORBIT_OFFSETQZSS &&
525 ii < CLOCKORBIT_OFFSETQZSS + _phaseBias.NumberOfSat[CLOCKORBIT_SATQZSS]) {
526 if (!phaseBiasFresh[CLOCKORBIT_SATQZSS]) continue;
527 sys = 'J';
528 flag = t_eph::LNAV;
529 }
530 else if (ii >= CLOCKORBIT_OFFSETSBAS &&
531 ii < CLOCKORBIT_OFFSETSBAS + _phaseBias.NumberOfSat[CLOCKORBIT_SATSBAS]) {
532 if (!phaseBiasFresh[CLOCKORBIT_SATSBAS]) continue;
533 sys = 'S';
534 flag = t_eph::SBASL1;
535 }
536 else if (ii >= CLOCKORBIT_OFFSETBDS &&
537 ii < CLOCKORBIT_OFFSETBDS + _phaseBias.NumberOfSat[CLOCKORBIT_SATBDS]) {
538 if (!phaseBiasFresh[CLOCKORBIT_SATBDS]) continue;
539 sys = 'C';
540 if (num < 6) {// GEO
541 flag = t_eph::D2;
542 }
543 else if (num > 58 && num < 63) { // GEO
544 flag = t_eph::D2;
545 }
546 else {
547 flag = t_eph::D1;
548 }
549 }
550 else {
551 continue;
552 }
553 t_satPhaseBias satPhaseBias;
554 satPhaseBias._prn.set(sys, num, flag);
555 satPhaseBias._staID = _staID.toStdString();
556 satPhaseBias._time = _lastTime;
557 satPhaseBias._updateInt = _phaseBias.UpdateInterval;
558 satPhaseBias._ssrFormat = (_type == RTCMssr) ? ssrRtcmOld :
559 (_type == RTCMnewssr) ? ssrRtcmNew : ssrUnknown;
560 if (satPhaseBias._ssrFormat == e_ssrFormat::ssrRtcmNew) {
561 satPhaseBias._satYawInfoInd = _phaseBias.SatelliteYawInformationIndicator;
562 satPhaseBias._extPBPhaseInd = _phaseBias.ExtendedPhaseBiasPropertyID;
563 }
564 else {
565 satPhaseBias._dispBiasConsistInd = _phaseBias.DispersiveBiasConsistencyIndicator;
566 satPhaseBias._MelbWuebConsistInd = _phaseBias.MWConsistencyIndicator;
567 }
568 satPhaseBias._yaw = _phaseBias.Sat[ii].YawAngle;
569 satPhaseBias._yawRate = _phaseBias.Sat[ii].YawRate;
570 for (unsigned jj = 0; jj < _phaseBias.Sat[ii].NumberOfPhaseBiases; jj++) {
571 const SsrCorr::PhaseBias::PhaseBiasSat::PhaseBiasEntry& biasEntry = _phaseBias.Sat[ii].Biases[jj];
572 t_frqPhaseBias frqPhaseBias;
573 frqPhaseBias._rnxType2ch.assign(_ssrCorr->codeTypeToRnxType(sys, biasEntry.Type));
574 frqPhaseBias._value = biasEntry.Bias;
575 frqPhaseBias._fixIndicator = biasEntry.IntegerIndicator;
576 if (satPhaseBias._ssrFormat == e_ssrFormat::ssrRtcmNew) {
577 if (satPhaseBias._extPBPhaseInd) {
578 frqPhaseBias._fixWideLaneIndicator = biasEntry.WidelaneGroupIndicator;
579 }
580 }
581 else {
582 frqPhaseBias._fixWideLaneIndicator = biasEntry.WidelaneGroupIndicator;
583 }
584 frqPhaseBias._jumpCounter = biasEntry.DiscontinuityCounter;
585 if (!frqPhaseBias._rnxType2ch.empty()) {
586 satPhaseBias._bias.push_back(frqPhaseBias);
587 }
588 }
589 _phaseBiases[_lastTime].append(satPhaseBias);
590 }
591
592 // Ionospheric Model
593 // -----------------
594 if (_vTEC.NumLayers > 0) {
595 _vTecMap[_lastTime]._time = _lastTime;
596 _vTecMap[_lastTime]._updateInt = _vTEC.UpdateInterval;
597 _vTecMap[_lastTime]._staID = _staID.toStdString();
598 for (unsigned ii = 0; ii < _vTEC.NumLayers; ii++) {
599 const SsrCorr::VTEC::IonoLayers& ionoLayer = _vTEC.Layers[ii];
600 t_vTecLayer layer;
601 layer._height = ionoLayer.Height;
602 layer._C.ReSize(ionoLayer.Degree+1, ionoLayer.Order+1);
603 layer._S.ReSize(ionoLayer.Degree+1, ionoLayer.Order+1);
604 for (unsigned iDeg = 0; iDeg <= ionoLayer.Degree; iDeg++) {
605 for (unsigned iOrd = 0; iOrd <= ionoLayer.Order; iOrd++) {
606 layer._C[iDeg][iOrd] = ionoLayer.Cosinus[iDeg][iOrd];
607 layer._S[iDeg][iOrd] = ionoLayer.Sinus[iDeg][iOrd];
608 }
609 }
610 _vTecMap[_lastTime]._layers.push_back(layer);
611 }
612 }
613
614 // Metadata (model correction information)
615 // ----------------------------------------
616 if (_metaData.NumEntries > 0) {
617 // Metadata carries no epoch field of its own (it is keyed here by
618 // _lastTime, inherited from whichever other message last set it), so a
619 // fresh, complete message must replace rather than accumulate onto
620 // whatever a previous message at the same _lastTime already stored.
621 _metaDataMap[_lastTime] = t_metaData();
622 _metaDataMap[_lastTime]._time = _lastTime;
623 _metaDataMap[_lastTime]._staID = _staID.toStdString();
624 _metaDataMap[_lastTime]._ssrIOD = _metaData.SSRIOD;
625 _metaDataMap[_lastTime]._providerID = _metaData.SSRProviderID;
626 _metaDataMap[_lastTime]._solutionID = _metaData.SSRSolutionID;
627 for (unsigned ii = 0; ii < _metaData.NumEntries; ii++) {
628 const SsrCorr::MetaData::ModelPart& part = _metaData.Entries[ii];
629 t_metaDataEntry entry;
630 entry._typeIndicator = part.TypeIndicator;
631 entry._applicationIndicator = part.ApplicationIndicator;
632 entry._nonDefaultIndicator = part.nonDefaultIndicator;
633 entry._nonDefaultIdentifier = part.nonDefaultIdentifier;
634 entry._dataIODIndicator = part.DataIODIndicator;
635 entry._dataIOD = part.DataIOD;
636 _metaDataMap[_lastTime]._entries.push_back(entry);
637 }
638 }
639
640 // Dump all older epochs
641 // ---------------------
642 QMutableMapIterator<bncTime, QList<t_orbCorr> > itOrb(_orbCorrections);
643 while (itOrb.hasNext()) {
644 itOrb.next();
645 if (itOrb.key() < _lastTime) {
646 emit newOrbCorrections(itOrb.value());
647 t_orbCorr::writeEpoch(_out, itOrb.value());
648 itOrb.remove();
649 }
650 }
651 QMutableMapIterator<bncTime, QList<t_clkCorr> > itClk(_clkCorrections);
652 while (itClk.hasNext()) {
653 itClk.next();
654 if (itClk.key() < _lastTime) {
655 emit newClkCorrections(itClk.value());
656 t_clkCorr::writeEpoch(_out, itClk.value());
657 itClk.remove();
658 }
659 }
660 QMutableMapIterator<bncTime, QList<t_satCodeBias> > itCB(_codeBiases);
661 while (itCB.hasNext()) {
662 itCB.next();
663 if (itCB.key() < _lastTime) {
664 emit newCodeBiases(itCB.value());
665 t_satCodeBias::writeEpoch(_out, itCB.value());
666 itCB.remove();
667 }
668 }
669 QMutableMapIterator<bncTime, QList<t_satPhaseBias> > itPB(_phaseBiases);
670 while (itPB.hasNext()) {
671 itPB.next();
672 if (itPB.key() < _lastTime) {
673 emit newPhaseBiases(itPB.value());
674 t_satPhaseBias::writeEpoch(_out, itPB.value());
675 itPB.remove();
676 }
677 }
678 QMutableMapIterator<bncTime, t_vTec> itTec(_vTecMap);
679 while (itTec.hasNext()) {
680 itTec.next();
681 if (itTec.key() < _lastTime) {
682 emit newTec(itTec.value());
683 t_vTec::write(_out, itTec.value());
684 itTec.remove();
685 }
686 }
687 QMutableMapIterator<bncTime, t_metaData> itMD(_metaDataMap);
688 while (itMD.hasNext()) {
689 itMD.next();
690 if (itMD.key() < _lastTime) {
691 emit newMetaData(itMD.value());
692 t_metaData::write(_out, itMD.value());
693 itMD.remove();
694 }
695 }
696}
697
698//
699////////////////////////////////////////////////////////////////////////////
700void RTCM3coDecoder::checkProviderID() {
701
702 if (_clkOrb.SSRProviderID == 0 && _clkOrb.SSRSolutionID == 0 && _clkOrb.SSRIOD == 0) {
703 return;
704 }
705
706 int newProviderID[3];
707 newProviderID[0] = _clkOrb.SSRProviderID;
708 newProviderID[1] = _clkOrb.SSRSolutionID;
709 newProviderID[2] = _clkOrb.SSRIOD;
710 QString newProviderIDStr = QString(" [SSR Provider ID: %1 SSR Solution ID: %2 SSR IOD: %3]: ")
711 .arg(newProviderID[0]).arg(newProviderID[1]).arg(newProviderID[2]);
712
713 bool alreadySet = false;
714 bool different = false;
715
716 for (unsigned ii = 0; ii < 3; ii++) {
717 if (_providerID[ii] != -1) {
718 alreadySet = true;
719 }
720 if (_providerID[ii] != newProviderID[ii]) {
721 different = true;
722 }
723 _providerID[ii] = newProviderID[ii];
724 }
725
726 if (alreadySet && different) {
727 emit newMessage("RTCM3coDecoder: SSR Provider or Service changed " + newProviderIDStr.toLatin1() + _staID.toLatin1(), true);
728 emit providerIDChanged(_staID);
729 }
730}
731
732// Check corrections
733////////////////////////////////////////////////////////////////////////////
734bool RTCM3coDecoder::corrIsOutOfRange(const SsrCorr::ClockOrbit::SatData& coSat) {
735
736 QString ssrParStr;
737 QString ssrParValue;
738 bool corrIsOutOfRange = false;
739
740 switch (_type) {
741 // ======== //
742 // IGS SSR //
743 // ======== //
744 case IGSssr:
745 if (coSat.Clock.DeltaA0 < -209.7151 ||
746 coSat.Clock.DeltaA0 > +209.7151) {
747 ssrParStr = "Clock::DeltaA0";
748 ssrParValue = QString::number(coSat.Clock.DeltaA0, 'f', 4);
749 corrIsOutOfRange = true;
750 }
751 if (coSat.Clock.DeltaA1 < -1.048575 ||
752 coSat.Clock.DeltaA1 > +1.048575) {
753 ssrParStr = "Clock::DeltaA1";
754 ssrParValue = QString::number(coSat.Clock.DeltaA1, 'f', 6);
755 corrIsOutOfRange = true;
756 }
757 if (coSat.Clock.DeltaA2 < -1.3421772 ||
758 coSat.Clock.DeltaA2 > +1.3421772) {
759 ssrParStr = "Clock::DeltaA2";
760 ssrParValue = QString::number(coSat.Clock.DeltaA2, 'f', 7);
761 corrIsOutOfRange = true;
762 }
763
764 if (coSat.Orbit.DeltaRadial < -209.7151 ||
765 coSat.Orbit.DeltaRadial > +209.7151) {
766 ssrParStr = "Orbit::DeltaRadial";
767 ssrParValue = QString::number(coSat.Orbit.DeltaRadial, 'f', 4);
768 corrIsOutOfRange = true;
769 }
770
771 if (coSat.Orbit.DeltaAlongTrack < -209.7148 ||
772 coSat.Orbit.DeltaAlongTrack > +209.7148) {
773 ssrParStr = "Orbit::DeltaAlongTrack";
774 ssrParValue = QString::number(coSat.Orbit.DeltaAlongTrack, 'f', 4);
775 corrIsOutOfRange = true;
776 }
777 if (coSat.Orbit.DeltaCrossTrack < -209.7148 ||
778 coSat.Orbit.DeltaCrossTrack > +209.7148) {
779 ssrParStr = "Orbit::DeltaCrossTrack";
780 ssrParValue = QString::number(coSat.Orbit.DeltaCrossTrack, 'f', 4);
781 corrIsOutOfRange = true;
782 }
783
784 if (coSat.Orbit.DotDeltaRadial < -1.048575 ||
785 coSat.Orbit.DotDeltaRadial > +1.048575) {
786 ssrParStr = "Orbit::DotDeltaRadial";
787 ssrParValue = QString::number(coSat.Orbit.DotDeltaRadial, 'f', 6);
788 corrIsOutOfRange = true;
789 }
790 if (coSat.Orbit.DotDeltaAlongTrack < -1.048572 ||
791 coSat.Orbit.DotDeltaAlongTrack > +1.048572) {
792 ssrParStr = "Orbit::DotDeltaAlongTrack";
793 ssrParValue = QString::number(coSat.Orbit.DotDeltaAlongTrack, 'f', 6);
794 corrIsOutOfRange = true;
795 }
796 if (coSat.Orbit.DotDeltaCrossTrack < -1.048572 ||
797 coSat.Orbit.DotDeltaCrossTrack > +1.048572) {
798 ssrParStr = "Orbit::DotDeltaCrossTrack";
799 ssrParValue = QString::number(coSat.Orbit.DotDeltaCrossTrack, 'f', 6);
800 corrIsOutOfRange = true;
801 }
802 break;
803 //==========//
804 // RTCM SSR //
805 // =========//
806 case RTCMssr:
807 case RTCMnewssr:
808 if (coSat.Clock.DeltaA0 < -209.7151 ||
809 coSat.Clock.DeltaA0 > +209.7151) {
810 ssrParStr = "Clock::DeltaA0";
811 ssrParValue = QString::number(coSat.Clock.DeltaA0, 'f', 4);
812 corrIsOutOfRange = true;
813 }
814 if (coSat.Clock.DeltaA1 < -1.048575 ||
815 coSat.Clock.DeltaA1 > +1.048575) {
816 ssrParStr = "Clock::DeltaA1";
817 ssrParValue = QString::number(coSat.Clock.DeltaA1, 'f', 6);
818 corrIsOutOfRange = true;
819 }
820 if (coSat.Clock.DeltaA2 < -1.34217726 ||
821 coSat.Clock.DeltaA2 > +1.34217726) {
822 ssrParStr = "Clock::DeltaA2";
823 ssrParValue = QString::number(coSat.Clock.DeltaA2, 'f', 8);
824 corrIsOutOfRange = true;
825 }
826
827 if (coSat.Orbit.DeltaRadial < -209.7151 ||
828 coSat.Orbit.DeltaRadial > +209.7151) {
829 ssrParStr = "Orbit::DeltaRadial";
830 ssrParValue = QString::number(coSat.Orbit.DeltaRadial, 'f', 4);
831 corrIsOutOfRange = true;
832 }
833
834 if (coSat.Orbit.DeltaAlongTrack < -209.7148 ||
835 coSat.Orbit.DeltaAlongTrack > +209.7148) {
836 ssrParStr = "Orbit::DeltaAlongTrack";
837 ssrParValue = QString::number(coSat.Orbit.DeltaAlongTrack, 'f', 4);
838 corrIsOutOfRange = true;
839 }
840 if (coSat.Orbit.DeltaCrossTrack < -209.7148 ||
841 coSat.Orbit.DeltaCrossTrack > +209.7148) {
842 ssrParStr = "Orbit::DeltaCrossTrack";
843 ssrParValue = QString::number(coSat.Orbit.DeltaCrossTrack, 'f', 4);
844 corrIsOutOfRange = true;
845 }
846
847 if (coSat.Orbit.DotDeltaRadial < -1.048575 ||
848 coSat.Orbit.DotDeltaRadial > +1.048575) {
849 ssrParStr = "Orbit::DotDeltaRadial";
850 ssrParValue = QString::number(coSat.Orbit.DotDeltaRadial, 'f', 6);
851 corrIsOutOfRange = true;
852 }
853 if (coSat.Orbit.DotDeltaAlongTrack < -1.048572 ||
854 coSat.Orbit.DotDeltaAlongTrack > +1.048572) {
855 ssrParStr = "Orbit::DotDeltaAlongTrack";
856 ssrParValue = QString::number(coSat.Orbit.DotDeltaAlongTrack, 'f', 6);
857 corrIsOutOfRange = true;
858 }
859 if (coSat.Orbit.DotDeltaCrossTrack < -1.048572 ||
860 coSat.Orbit.DotDeltaCrossTrack > +1.048572) {
861 ssrParStr = "Orbit::DotDeltaCrossTrack";
862 ssrParValue = QString::number(coSat.Orbit.DotDeltaCrossTrack, 'f', 6);
863 corrIsOutOfRange = true;
864 }
865 break;
866 }
867
868 if (corrIsOutOfRange) {
869 emit newMessage("RTCM3coDecoder: Correction " + ssrParStr.toLatin1()
870 + " (" + ssrParValue.toLatin1() + ") "
871 + "is out of range " + _staID.toLatin1(), true);
872 }
873
874 return corrIsOutOfRange;
875}
876
877//
878////////////////////////////////////////////////////////////////////////////
879void RTCM3coDecoder::setEpochTime() {
880
881 // _phaseBias is no longer wiped between messages (kept alive so a later
882 // Extended Phase Bias message can correlate against it), so
883 // NumberOfSat[s] > 0 alone no longer means "phase bias was just decoded
884 // this cycle" - it can be stale from many messages ago. Gate each
885 // phase-bias fallback below on the same per-system freshness tracking
886 // sendResults() uses (_phaseBiasSentEpoch), so a stale leftover value
887 // never gets picked as this cycle's epoch.
888 bncTime prevTime = _lastTime;
889 _lastTime.reset();
890
891 double epoSecGPS = -1.0;
892 double epoSecGlo = -1.0;
893 double epoSecGal = -1.0;
894 double epoSecQzss = -1.0;
895 double epoSecSbas = -1.0;
896 double epoSecBds = -1.0;
897 if (_clkOrb.NumberOfSat[CLOCKORBIT_SATGPS] > 0) {
898 epoSecGPS = _clkOrb.EpochTime[CLOCKORBIT_SATGPS]; // 0 .. 604799 s
899 }
900 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATGPS] > 0) {
901 epoSecGPS = _codeBias.EpochTime[CLOCKORBIT_SATGPS]; // 0 .. 604799 s
902 }
903 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGPS] > 0 &&
904 _phaseBias.EpochTime[CLOCKORBIT_SATGPS] != _phaseBiasSentEpoch[CLOCKORBIT_SATGPS]) {
905 epoSecGPS = _phaseBias.EpochTime[CLOCKORBIT_SATGPS]; // 0 .. 604799 s
906 }
907 else if (_vTEC.NumLayers > 0) {
908 epoSecGPS = _vTEC.EpochTime; // 0 .. 604799 s
909 }
910 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATGLONASS] > 0) {
911 epoSecGlo = _clkOrb.EpochTime[CLOCKORBIT_SATGLONASS]; // 0 .. 86399 s
912 }
913 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATGLONASS] > 0) {
914 epoSecGlo = _codeBias.EpochTime[CLOCKORBIT_SATGLONASS]; // 0 .. 86399 s
915 }
916 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGLONASS] > 0 &&
917 _phaseBias.EpochTime[CLOCKORBIT_SATGLONASS] != _phaseBiasSentEpoch[CLOCKORBIT_SATGLONASS]) {
918 epoSecGlo = _phaseBias.EpochTime[CLOCKORBIT_SATGLONASS]; // 0 .. 86399 s
919 }
920 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATGALILEO] > 0) {
921 epoSecGal = _clkOrb.EpochTime[CLOCKORBIT_SATGALILEO];
922 }
923 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATGALILEO] > 0) {
924 epoSecGal = _codeBias.EpochTime[CLOCKORBIT_SATGALILEO];
925 }
926 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGALILEO] > 0 &&
927 _phaseBias.EpochTime[CLOCKORBIT_SATGALILEO] != _phaseBiasSentEpoch[CLOCKORBIT_SATGALILEO]) {
928 epoSecGal = _phaseBias.EpochTime[CLOCKORBIT_SATGALILEO];
929 }
930 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATQZSS] > 0) {
931 epoSecQzss = _clkOrb.EpochTime[CLOCKORBIT_SATQZSS];
932 }
933 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATQZSS] > 0) {
934 epoSecQzss = _codeBias.EpochTime[CLOCKORBIT_SATQZSS];
935 }
936 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATQZSS] > 0 &&
937 _phaseBias.EpochTime[CLOCKORBIT_SATQZSS] != _phaseBiasSentEpoch[CLOCKORBIT_SATQZSS]) {
938 epoSecQzss = _phaseBias.EpochTime[CLOCKORBIT_SATQZSS];
939 }
940 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATSBAS] > 0) {
941 epoSecSbas = _clkOrb.EpochTime[CLOCKORBIT_SATSBAS];
942 }
943 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATSBAS] > 0) {
944 epoSecSbas = _codeBias.EpochTime[CLOCKORBIT_SATSBAS];
945 }
946 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATSBAS] > 0 &&
947 _phaseBias.EpochTime[CLOCKORBIT_SATSBAS] != _phaseBiasSentEpoch[CLOCKORBIT_SATSBAS]) {
948 epoSecSbas = _phaseBias.EpochTime[CLOCKORBIT_SATSBAS];
949 }
950 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATBDS] > 0) {
951 epoSecBds = _clkOrb.EpochTime[CLOCKORBIT_SATBDS];
952 }
953 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATBDS] > 0) {
954 epoSecBds = _codeBias.EpochTime[CLOCKORBIT_SATBDS];
955 }
956 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATBDS] > 0 &&
957 _phaseBias.EpochTime[CLOCKORBIT_SATBDS] != _phaseBiasSentEpoch[CLOCKORBIT_SATBDS]) {
958 epoSecBds = _phaseBias.EpochTime[CLOCKORBIT_SATBDS];
959 }
960
961 // Retrieve current time
962 // ---------------------
963 int currentWeek = 0;
964 double currentSec = 0.0;
965 currentGPSWeeks(currentWeek, currentSec);
966 bncTime currentTime(currentWeek, currentSec);
967
968 // Set _lastTime close to currentTime
969 // ----------------------------------
970 if (epoSecGPS != -1) {
971 _lastTime.set(currentWeek, epoSecGPS);
972 }
973 else if (epoSecGlo != -1) {
974 QDate date = dateAndTimeFromGPSweek(currentTime.gpsw(), currentTime.gpssec()).date();
975 if (_type == IGSssr) {
976 epoSecGlo = epoSecGlo + gnumleap(date.year(), date.month(), date.day());
977 }
978 if (_type == RTCMssr || _type == RTCMnewssr) {
979 // Same GLONASS epoch wire convention (UTC+3h) as RTCMssr
980 epoSecGlo = epoSecGlo - 3 * 3600 + gnumleap(date.year(), date.month(), date.day());
981 }
982 _lastTime.set(currentWeek, epoSecGlo);
983 }
984 else if (epoSecGal != -1) {
985 _lastTime.set(currentWeek, epoSecGal);
986 }
987 else if (epoSecQzss != -1) {
988 _lastTime.set(currentWeek, epoSecQzss);
989 }
990 else if (epoSecSbas != -1) {
991 _lastTime.set(currentWeek, epoSecSbas);
992 }
993 else if (epoSecBds != -1) {
994 epoSecBds += 14.0;
995 if (epoSecBds > 604800.0) {
996 epoSecBds -= 7.0*24.0*60.0*60.0;
997 }
998 _lastTime.set(currentWeek, epoSecBds);
999 }
1000 // Some message types (e.g. Metadata) carry no epoch field of their own,
1001 // and none of the above found anything fresh to use either - fall back
1002 // to the last known valid time rather than leaving _lastTime invalid
1003 // (which would silently drop the message's output entirely).
1004 else if (prevTime.valid()) {
1005 _lastTime = prevTime;
1006 }
1007
1008 if (_lastTime.valid()) {
1009 double maxDiff = 12 * 3600.0;
1010 while (_lastTime < currentTime - maxDiff) {
1011 _lastTime = _lastTime + maxDiff;
1012 }
1013 while (_lastTime > currentTime + maxDiff) {
1014 _lastTime = _lastTime - maxDiff;
1015 }
1016 }
1017}
Note: See TracBrowser for help on using the repository browser.