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

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

updates regarding RTCM-SSR and some ovarall improvements

File size: 47.2 KB
RevLine 
[866]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)
[11024]6// http://bkg.bund.de
[866]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 *
[6215]37 * Changes:
[866]38 *
39 * -----------------------------------------------------------------------*/
40
[868]41#include <stdio.h>
[920]42#include <math.h>
[11010]43#include <climits>
[868]44
[866]45#include "RTCM3coDecoder.h"
[918]46#include "bncutils.h"
[934]47#include "bncrinex.h"
[5070]48#include "bnccore.h"
[1535]49#include "bncsettings.h"
[4428]50#include "bnctime.h"
[866]51
52using namespace std;
53
54// Constructor
55////////////////////////////////////////////////////////////////////////////
[9048]56RTCM3coDecoder::RTCM3coDecoder(const QString& staID) {
[934]57
[970]58 _staID = staID;
59
[934]60 // File Output
61 // -----------
[1535]62 bncSettings settings;
[934]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 }
[970]69 _fileNameSkl = path + staID;
[934]70 }
[6141]71 _out = 0;
[934]72
[6215]73 connect(this, SIGNAL(newOrbCorrections(QList<t_orbCorr>)),
[6141]74 BNC_CORE, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)));
[1828]75
[6215]76 connect(this, SIGNAL(newClkCorrections(QList<t_clkCorr>)),
[6141]77 BNC_CORE, SLOT(slotNewClkCorrections(QList<t_clkCorr>)));
78
[6486]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
[11008]88 connect(this, SIGNAL(newMetaData(t_metaData)),
89 BNC_CORE, SLOT(slotNewMetaData(t_metaData)));
90
[11038]91 connect(this, SIGNAL(newSatAntennas(QList<t_satAntenna>)),
92 BNC_CORE, SLOT(slotNewSatAntennas(QList<t_satAntenna>)));
93
[6215]94 connect(this, SIGNAL(providerIDChanged(QString)),
[5577]95 BNC_CORE, SIGNAL(providerIDChanged(QString)));
96
[6215]97 connect(this, SIGNAL(newMessage(QByteArray,bool)),
[5068]98 BNC_CORE, SLOT(slotMessage(const QByteArray,bool)));
[4428]99
[6467]100 reset();
[5576]101
102 _providerID[0] = -1;
103 _providerID[1] = -1;
104 _providerID[2] = -1;
[9025]105
[11038]106 for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
[11047]107 _antennaReceived[s] = false;
108 _antennaIODKnown[s] = false;
109 _antennaIODProvider[s] = 0;
110 _antennaIODCur[s] = 0;
111 _antennaIODPrev[s] = 0;
[11038]112 }
[11047]113 for (unsigned i = 0; i < CLOCKORBIT_COUNTSAT; i++) {
114 _antennaSentProviderID[i] = 0;
115 _antennaSentIOD[i] = 0; // meaningless while _antennaSentTime[i] is invalid ("never sent")
116 }
[11038]117
118 _stateSnapshot = new t_stateSnapshot;
[9025]119 _ssrCorr = 0;
[10534]120
[866]121}
122
123// Destructor
124////////////////////////////////////////////////////////////////////////////
125RTCM3coDecoder::~RTCM3coDecoder() {
[935]126 delete _out;
[9025]127 delete _ssrCorr;
[11038]128 delete _stateSnapshot;
[7641]129 _IODs.clear();
130 _orbCorrections.clear();
131 _clkCorrections.clear();
132 _lastClkCorrections.clear();
133 _codeBiases.clear();
134 _phaseBiases.clear();
135 _vTecMap.clear();
[11008]136 _metaDataMap.clear();
[11038]137 _satAntennas.clear();
[866]138}
139
[7641]140//
[6467]141////////////////////////////////////////////////////////////////////////////
[11002]142void RTCM3coDecoder::reset(bool resetPhaseBias) {
[6467]143 memset(&_clkOrb, 0, sizeof(_clkOrb));
144 memset(&_codeBias, 0, sizeof(_codeBias));
[11002]145 if (resetPhaseBias) {
146 memset(&_phaseBias, 0, sizeof(_phaseBias));
[11010]147 for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
148 _phaseBiasSentEpoch[s] = UINT_MAX; // "never sent"
149 }
[11002]150 }
[6467]151 memset(&_vTEC, 0, sizeof(_vTEC));
152}
153
[934]154// Reopen Output File
[6215]155////////////////////////////////////////////////////////////////////////
[6141]156void RTCM3coDecoder::reopen() {
[934]157
[6141]158 if (!_fileNameSkl.isEmpty()) {
[934]159
[1535]160 bncSettings settings;
[934]161
[1154]162 QDateTime datTim = currentDateAndTimeGPS();
[934]163
164 QString hlpStr = bncRinex::nextEpochStr(datTim,
[9771]165 settings.value("corrIntr").toString(), 3);
[934]166
[9941]167 QString cntStr = (_fileNameSkl.contains("ION")) ? "_ION.ssr" : "_MC.ssr";
168
[9771]169 QString fileNameHlp = _fileNameSkl +
170 "_S_" + // stream
171 QString("%1").arg(datTim.date().year()) +
172 QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
[9941]173 hlpStr + // HM_period
174 cntStr; // mixed ION or CLK
[934]175
[6141]176 if (_fileName == fileNameHlp) {
[934]177 return;
178 }
179 else {
[6141]180 _fileName = fileNameHlp;
[934]181 }
182
[6141]183 delete _out;
[1727]184 if ( Qt::CheckState(settings.value("rnxAppend").toInt()) == Qt::Checked) {
[8204]185 _out = new ofstream( _fileName.toLatin1().data(), ios_base::out | ios_base::app );
[1727]186 }
187 else {
[8204]188 _out = new ofstream( _fileName.toLatin1().data() );
[1727]189 }
[934]190 }
191}
192
[6215]193//
[866]194////////////////////////////////////////////////////////////////////////////
[1227]195t_irc RTCM3coDecoder::Decode(char* buffer, int bufLen, vector<string>& errmsg) {
[868]196
[1218]197 errmsg.clear();
198
[1227]199 _buffer.append(QByteArray(buffer,bufLen));
[868]200
[1023]201 t_irc retCode = failure;
202
[1832]203 while(_buffer.size()) {
204
[11004]205 // save state
[11038]206 memcpy(&_stateSnapshot->clkOrb, &_clkOrb, sizeof(_clkOrb));
207 memcpy(&_stateSnapshot->codeBias, &_codeBias, sizeof(_codeBias));
208 memcpy(&_stateSnapshot->phaseBias, &_phaseBias, sizeof(_phaseBias));
209 memcpy(&_stateSnapshot->vTEC, &_vTEC, sizeof(_vTEC));
210 memcpy(&_stateSnapshot->metaData, &_metaData, sizeof(_metaData));
211 memcpy(&_stateSnapshot->antenna, &_antenna, sizeof(_antenna));
[6454]212
[11047]213 // _antenna persists across messages, so its content alone can't tell
214 // whether a Satellite Antenna message arrived in this frame (an unchanged
215 // retransmission looks identical). Clear messageType[] as a marker: the
216 // COBOFS_SATANT decoder sets it (always non-zero) for each system it
217 // decodes. Restored with the snapshot if the frame turns out incomplete.
218 for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
219 _antenna.messageType[s] = 0;
220 }
221
[879]222 int bytesused = 0;
[9306]223
[11031]224 GCOB_RETURN irc = _ssrCorr->GetSSR(&_clkOrb, &_codeBias, &_vTEC, &_phaseBias, &_metaData, &_antenna,
[9306]225 _buffer.data(), _buffer.size(), &bytesused);
[1829]226
[1833]227 if (irc <= -30) { // not enough data - restore state and exit loop
[11038]228 memcpy(&_clkOrb, &_stateSnapshot->clkOrb, sizeof(_clkOrb));
229 memcpy(&_codeBias, &_stateSnapshot->codeBias, sizeof(_codeBias));
230 memcpy(&_phaseBias, &_stateSnapshot->phaseBias, sizeof(_phaseBias));
231 memcpy(&_vTEC, &_stateSnapshot->vTEC, sizeof(_vTEC));
232 memcpy(&_metaData, &_stateSnapshot->metaData, sizeof(_metaData));
[11040]233 memcpy(&_antenna, &_stateSnapshot->antenna, sizeof(_antenna));
[1833]234 break;
[869]235 }
[1832]236
[11003]237 else if (irc < 0) { // error - skip this message (or 1 byte if the
[11004]238 // frame itself couldn't be recognized) and retry
[11003]239 if (irc != GCOBR_UNKNOWNTYPE) {
240 // A recognized-but-unimplemented message (e.g. Tropospheric or
241 // Regional Ionospheric Correction) leaves _clkOrb/_codeBias/
242 // _phaseBias/_vTEC untouched, and bytesused is already reliably set
243 // from its CRC-validated frame length, so there is nothing to
244 // discard. Every other error code may have left inconsistent
245 // partial state (e.g. mid-satellite-loop) or an unreliable frame
246 // boundary, so reset defensively there. Without this, streams with
247 // frequent unimplemented messages (e.g. dense grid-based Regional
248 // Ionospheric traffic) would wipe unrelated, still-valid orbit/
249 // clock/bias/phase-bias state every time one is skipped.
250 reset();
251 }
[1842]252 _buffer = _buffer.mid(bytesused ? bytesused : 1);
[1833]253 }
254
255 else { // OK or MESSAGEFOLLOWS
[1828]256 _buffer = _buffer.mid(bytesused);
257
[6467]258 if (irc == GCOBR_OK || irc == GCOBR_MESSAGEFOLLOWS ) {
[11047]259 // Latch the reception until sendResults() consumes it - it may not
260 // run for this frame if _lastTime is still invalid.
261 for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
262 if (_antenna.messageType[s] != 0) {
263 _antennaReceived[s] = true;
264 checkAntennaIOD(s);
265 }
266 }
[6556]267 setEpochTime(); // sets _lastTime
[7641]268
269 if (_lastTime.valid()) {
[6467]270 reopen();
271 checkProviderID();
272 sendResults();
273 retCode = success;
[919]274 }
[6467]275 else {
[10527]276 emit newMessage("RTCM3coDecoder: _lastTime invalid: " + _staID.toLatin1(), true);
[6467]277 retCode = failure;
[1829]278 }
[11002]279 // Keep _phaseBias intact: an Extended Phase Bias message (COBOFS_PBEXT)
280 // arriving later needs to correlate against the satellite/signal list
281 // just published by this Phase Bias message. clock_orbit_rtcm_new.cpp's
282 // COBOFS_PBIAS handling resets pb->NumberOfSat[s] itself before decoding
283 // a fresh message, so this doesn't cause stale data to accumulate.
284 reset(false);
[869]285 }
[1829]286 }
[1833]287 }
[1832]288
[1829]289 return retCode;
[866]290}
[934]291
[6215]292//
[934]293////////////////////////////////////////////////////////////////////////////
[6141]294void RTCM3coDecoder::sendResults() {
[934]295
[6455]296 // Orbit and clock corrections of all satellites
297 // ---------------------------------------------
[6854]298 for (unsigned ii = 0; ii < CLOCKORBIT_NUMGPS
299 + CLOCKORBIT_NUMGLONASS
300 + CLOCKORBIT_NUMGALILEO
301 + CLOCKORBIT_NUMQZSS
302 + CLOCKORBIT_NUMSBAS
303 + _clkOrb.NumberOfSat[CLOCKORBIT_SATBDS];
304 ii++) {
[10572]305 if (corrIsOutOfRange(_clkOrb.Sat[ii])) {
306 continue;
307 }
[10599]308 char sys = ' ';
309 int num = _clkOrb.Sat[ii].ID;
310 int flag = 0; // to force NAV type usage according SSR standard
[6454]311 if (ii < _clkOrb.NumberOfSat[CLOCKORBIT_SATGPS]) {
[10599]312 sys = 'G';
313 flag = t_eph::LNAV;
[3022]314 }
[6854]315 else if (ii >= CLOCKORBIT_OFFSETGLONASS &&
[6855]316 ii < CLOCKORBIT_OFFSETGLONASS + _clkOrb.NumberOfSat[CLOCKORBIT_SATGLONASS]) {
[10599]317 sys = 'R';
318 flag = t_eph::FDMA_M;
[3022]319 }
[6854]320 else if (ii >= CLOCKORBIT_OFFSETGALILEO &&
[6855]321 ii < CLOCKORBIT_OFFSETGALILEO + _clkOrb.NumberOfSat[CLOCKORBIT_SATGALILEO]) {
[10599]322 sys = 'E';
323 flag = t_eph::INAV;
[6854]324 }
325 else if (ii >= CLOCKORBIT_OFFSETQZSS &&
[6855]326 ii < CLOCKORBIT_OFFSETQZSS + _clkOrb.NumberOfSat[CLOCKORBIT_SATQZSS]) {
[10599]327 sys = 'J';
328 flag = t_eph::LNAV;
[6854]329 }
330 else if (ii >= CLOCKORBIT_OFFSETSBAS &&
[6855]331 ii < CLOCKORBIT_OFFSETSBAS + _clkOrb.NumberOfSat[CLOCKORBIT_SATSBAS]) {
[10599]332 sys = 'S';
333 flag = t_eph::SBASL1;
[6854]334 }
335 else if (ii >= CLOCKORBIT_OFFSETBDS &&
[6855]336 ii < CLOCKORBIT_OFFSETBDS + _clkOrb.NumberOfSat[CLOCKORBIT_SATBDS]) {
[10599]337 sys = 'C';
338 if (num < 6) {// GEO
339 flag = t_eph::D2;
340 }
341 else if (num > 58 && num < 63) { // GEO
342 flag = t_eph::D2;
343 }
344 else {
345 flag = t_eph::D1;
346 }
[6854]347 }
[6141]348 else {
349 continue;
350 }
[9025]351
[6141]352 // Orbit correction
353 // ----------------
[9025]354 if ( _clkOrb.messageType == _ssrCorr->COTYPE_GPSCOMBINED ||
355 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSCOMBINED ||
356 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOCOMBINED ||
357 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSCOMBINED ||
358 _clkOrb.messageType == _ssrCorr->COTYPE_SBASCOMBINED ||
359 _clkOrb.messageType == _ssrCorr->COTYPE_BDSCOMBINED ||
360 _clkOrb.messageType == _ssrCorr->COTYPE_GPSORBIT ||
361 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSORBIT ||
362 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOORBIT ||
363 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSORBIT ||
364 _clkOrb.messageType == _ssrCorr->COTYPE_SBASORBIT ||
365 _clkOrb.messageType == _ssrCorr->COTYPE_BDSORBIT ) {
[3022]366
[6141]367 t_orbCorr orbCorr;
[10599]368 orbCorr._prn.set(sys, num, flag);
[11047]369 checkPrnRange(orbCorr._prn);
[6564]370 orbCorr._staID = _staID.toStdString();
[6556]371 orbCorr._iod = _clkOrb.Sat[ii].IOD;
372 orbCorr._time = _lastTime;
373 orbCorr._updateInt = _clkOrb.UpdateInterval;
[10599]374 orbCorr._system = sys;
[6556]375 orbCorr._xr[0] = _clkOrb.Sat[ii].Orbit.DeltaRadial;
376 orbCorr._xr[1] = _clkOrb.Sat[ii].Orbit.DeltaAlongTrack;
377 orbCorr._xr[2] = _clkOrb.Sat[ii].Orbit.DeltaCrossTrack;
378 orbCorr._dotXr[0] = _clkOrb.Sat[ii].Orbit.DotDeltaRadial;
379 orbCorr._dotXr[1] = _clkOrb.Sat[ii].Orbit.DotDeltaAlongTrack;
380 orbCorr._dotXr[2] = _clkOrb.Sat[ii].Orbit.DotDeltaCrossTrack;
[10998]381 orbCorr._ssrFormat = (_type == RTCMssr) ? ssrRtcmOld :
382 (_type == RTCMnewssr) ? ssrRtcmNew : ssrUnknown;
[3022]383
[7652]384 _orbCorrections[_lastTime].append(orbCorr);
[3022]385
[6471]386 _IODs[orbCorr._prn] = _clkOrb.Sat[ii].IOD;
[6141]387 }
[3022]388
[6455]389 // Clock Corrections
390 // -----------------
[9025]391 if ( _clkOrb.messageType == _ssrCorr->COTYPE_GPSCOMBINED ||
392 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSCOMBINED ||
393 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOCOMBINED ||
394 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSCOMBINED ||
395 _clkOrb.messageType == _ssrCorr->COTYPE_SBASCOMBINED ||
396 _clkOrb.messageType == _ssrCorr->COTYPE_BDSCOMBINED ||
397 _clkOrb.messageType == _ssrCorr->COTYPE_GPSCLOCK ||
398 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSCLOCK ||
399 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOCLOCK ||
400 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSCLOCK ||
401 _clkOrb.messageType == _ssrCorr->COTYPE_SBASCLOCK ||
402 _clkOrb.messageType == _ssrCorr->COTYPE_BDSCLOCK) {
[3022]403
[6141]404 t_clkCorr clkCorr;
[10599]405 clkCorr._prn.set(sys, _clkOrb.Sat[ii].ID, flag);
[11047]406 checkPrnRange(clkCorr._prn);
[6564]407 clkCorr._staID = _staID.toStdString();
[6141]408 clkCorr._time = _lastTime;
[6556]409 clkCorr._updateInt = _clkOrb.UpdateInterval;
[7014]410 clkCorr._dClk = _clkOrb.Sat[ii].Clock.DeltaA0 / t_CST::c;
411 clkCorr._dotDClk = _clkOrb.Sat[ii].Clock.DeltaA1 / t_CST::c;
412 clkCorr._dotDotDClk = _clkOrb.Sat[ii].Clock.DeltaA2 / t_CST::c;
[3022]413
[6471]414 _lastClkCorrections[clkCorr._prn] = clkCorr;
415
416 if (_IODs.contains(clkCorr._prn)) {
417 clkCorr._iod = _IODs[clkCorr._prn];
[7652]418 _clkCorrections[_lastTime].append(clkCorr);
[3022]419 }
420 }
[6141]421
422 // High-Resolution Clocks
423 // ----------------------
[9025]424 if ( _clkOrb.messageType == _ssrCorr->COTYPE_GPSHR ||
425 _clkOrb.messageType == _ssrCorr->COTYPE_GLONASSHR ||
426 _clkOrb.messageType == _ssrCorr->COTYPE_GALILEOHR ||
427 _clkOrb.messageType == _ssrCorr->COTYPE_QZSSHR ||
428 _clkOrb.messageType == _ssrCorr->COTYPE_SBASHR ||
429 _clkOrb.messageType == _ssrCorr->COTYPE_BDSHR) {
[10599]430 t_prn prn(sys, _clkOrb.Sat[ii].ID, flag);
[6471]431 if (_lastClkCorrections.contains(prn)) {
432 t_clkCorr clkCorr;
[6556]433 clkCorr = _lastClkCorrections[prn];
434 clkCorr._time = _lastTime;
435 clkCorr._updateInt = _clkOrb.UpdateInterval;
436 clkCorr._dClk += _clkOrb.Sat[ii].hrclock / t_CST::c;
[6471]437 if (_IODs.contains(clkCorr._prn)) {
438 clkCorr._iod = _IODs[clkCorr._prn];
[10536]439 _clkCorrections[_lastTime].append(clkCorr);
[6471]440 }
441 }
[6141]442 }
[3022]443 }
444
[6455]445 // Code Biases
446 // -----------
[6854]447 for (unsigned ii = 0; ii < CLOCKORBIT_NUMGPS
448 + CLOCKORBIT_NUMGLONASS
449 + CLOCKORBIT_NUMGALILEO
450 + CLOCKORBIT_NUMQZSS
451 + CLOCKORBIT_NUMSBAS
452 + _codeBias.NumberOfSat[CLOCKORBIT_SATBDS];
453 ii++) {
[10599]454 char sys = ' ';
455 int num = _codeBias.Sat[ii].ID;
456 int flag = 0;
[6454]457 if (ii < _codeBias.NumberOfSat[CLOCKORBIT_SATGPS]) {
[10599]458 sys = 'G';
459 flag = t_eph::LNAV;
[3022]460 }
[6854]461 else if (ii >= CLOCKORBIT_OFFSETGLONASS &&
[6855]462 ii < CLOCKORBIT_OFFSETGLONASS + _codeBias.NumberOfSat[CLOCKORBIT_SATGLONASS]) {
[10599]463 sys = 'R';
464 flag = t_eph::FDMA_M;
[3022]465 }
[6854]466 else if (ii >= CLOCKORBIT_OFFSETGALILEO &&
[6855]467 ii < CLOCKORBIT_OFFSETGALILEO + _codeBias.NumberOfSat[CLOCKORBIT_SATGALILEO]) {
[10599]468 sys = 'E';
469 flag = t_eph::INAV;
[6854]470 }
471 else if (ii >= CLOCKORBIT_OFFSETQZSS &&
[6855]472 ii < CLOCKORBIT_OFFSETQZSS + _codeBias.NumberOfSat[CLOCKORBIT_SATQZSS]) {
[10599]473 sys = 'J';
474 flag = t_eph::LNAV;
[6854]475 }
476 else if (ii >= CLOCKORBIT_OFFSETSBAS &&
[6855]477 ii < CLOCKORBIT_OFFSETSBAS + _codeBias.NumberOfSat[CLOCKORBIT_SATSBAS]) {
[10599]478 sys = 'S';
479 flag = t_eph::SBASL1;
[6854]480 }
481 else if (ii >= CLOCKORBIT_OFFSETBDS &&
[6855]482 ii < CLOCKORBIT_OFFSETBDS + _codeBias.NumberOfSat[CLOCKORBIT_SATBDS]) {
[10599]483 sys = 'C';
484 if (num < 6) {// GEO
485 flag = t_eph::D2;
486 }
487 else if (num > 58 && num < 63) { // GEO
488 flag = t_eph::D2;
489 }
490 else {
491 flag = t_eph::D1;
492 }
[6854]493 }
[6141]494 else {
495 continue;
496 }
[6463]497 t_satCodeBias satCodeBias;
[10599]498 satCodeBias._prn.set(sys, num, flag);
[11047]499 checkPrnRange(satCodeBias._prn);
[6564]500 satCodeBias._staID = _staID.toStdString();
[6556]501 satCodeBias._time = _lastTime;
502 satCodeBias._updateInt = _codeBias.UpdateInterval;
[11047]503 satCodeBias._ssrIOD = _codeBias.SSRIOD;
504 satCodeBias._ssrProviderID = _codeBias.SSRProviderID;
[6454]505 for (unsigned jj = 0; jj < _codeBias.Sat[ii].NumberOfCodeBiases; jj++) {
[9025]506 const SsrCorr::CodeBias::BiasSat::CodeBiasEntry& biasEntry = _codeBias.Sat[ii].Biases[jj];
[6472]507 t_frqCodeBias frqCodeBias;
[10599]508 frqCodeBias._rnxType2ch.assign(_ssrCorr->codeTypeToRnxType(sys, biasEntry.Type));
[6472]509 frqCodeBias._value = biasEntry.Bias;
[6474]510 if (!frqCodeBias._rnxType2ch.empty()) {
511 satCodeBias._bias.push_back(frqCodeBias);
512 }
[6141]513 }
[7652]514 _codeBiases[_lastTime].append(satCodeBias);
[3022]515 }
516
[6487]517 // Phase Biases
518 // -----------
[11010]519 // _phaseBias is no longer wiped between messages (kept alive so a later
520 // Extended Phase Bias message can correlate against it - see
521 // COBOFS_PBEXT), so without this guard the block below would re-append
522 // the same system's satellites into the output on every other message
523 // decoded in between two actual phase-bias messages. Only emit a
524 // system's satellites once per epoch, tracked per system by comparing
525 // against the epoch we last actually sent for it.
526 bool phaseBiasFresh[CLOCKORBIT_SATNUM];
527 for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
528 phaseBiasFresh[s] = _phaseBias.NumberOfSat[s] > 0 &&
529 _phaseBias.EpochTime[s] != _phaseBiasSentEpoch[s];
530 if (phaseBiasFresh[s]) {
531 _phaseBiasSentEpoch[s] = _phaseBias.EpochTime[s];
532 }
533 }
[6854]534 for (unsigned ii = 0; ii < CLOCKORBIT_NUMGPS
535 + CLOCKORBIT_NUMGLONASS
536 + CLOCKORBIT_NUMGALILEO
537 + CLOCKORBIT_NUMQZSS
538 + CLOCKORBIT_NUMSBAS
539 + _phaseBias.NumberOfSat[CLOCKORBIT_SATBDS];
540 ii++) {
[10599]541 char sys = ' ';
542 int num = _phaseBias.Sat[ii].ID;
543 int flag = 0;
[6487]544 if (ii < _phaseBias.NumberOfSat[CLOCKORBIT_SATGPS]) {
[11010]545 if (!phaseBiasFresh[CLOCKORBIT_SATGPS]) continue;
[10599]546 sys = 'G';
547 flag = t_eph::LNAV;
[6487]548 }
[6854]549 else if (ii >= CLOCKORBIT_OFFSETGLONASS &&
[6855]550 ii < CLOCKORBIT_OFFSETGLONASS + _phaseBias.NumberOfSat[CLOCKORBIT_SATGLONASS]) {
[11010]551 if (!phaseBiasFresh[CLOCKORBIT_SATGLONASS]) continue;
[10599]552 sys = 'R';
553 flag = t_eph::FDMA_M;
[6487]554 }
[6854]555 else if (ii >= CLOCKORBIT_OFFSETGALILEO &&
[6855]556 ii < CLOCKORBIT_OFFSETGALILEO + _phaseBias.NumberOfSat[CLOCKORBIT_SATGALILEO]) {
[11010]557 if (!phaseBiasFresh[CLOCKORBIT_SATGALILEO]) continue;
[10599]558 sys = 'E';
559 flag = t_eph::INAV;
[6854]560 }
561 else if (ii >= CLOCKORBIT_OFFSETQZSS &&
[6855]562 ii < CLOCKORBIT_OFFSETQZSS + _phaseBias.NumberOfSat[CLOCKORBIT_SATQZSS]) {
[11010]563 if (!phaseBiasFresh[CLOCKORBIT_SATQZSS]) continue;
[10599]564 sys = 'J';
565 flag = t_eph::LNAV;
[6854]566 }
567 else if (ii >= CLOCKORBIT_OFFSETSBAS &&
[6855]568 ii < CLOCKORBIT_OFFSETSBAS + _phaseBias.NumberOfSat[CLOCKORBIT_SATSBAS]) {
[11010]569 if (!phaseBiasFresh[CLOCKORBIT_SATSBAS]) continue;
[10599]570 sys = 'S';
571 flag = t_eph::SBASL1;
[6854]572 }
573 else if (ii >= CLOCKORBIT_OFFSETBDS &&
[6855]574 ii < CLOCKORBIT_OFFSETBDS + _phaseBias.NumberOfSat[CLOCKORBIT_SATBDS]) {
[11010]575 if (!phaseBiasFresh[CLOCKORBIT_SATBDS]) continue;
[10599]576 sys = 'C';
577 if (num < 6) {// GEO
578 flag = t_eph::D2;
579 }
580 else if (num > 58 && num < 63) { // GEO
581 flag = t_eph::D2;
582 }
583 else {
584 flag = t_eph::D1;
585 }
[6854]586 }
[6487]587 else {
588 continue;
589 }
590 t_satPhaseBias satPhaseBias;
[10599]591 satPhaseBias._prn.set(sys, num, flag);
[11047]592 checkPrnRange(satPhaseBias._prn);
[6564]593 satPhaseBias._staID = _staID.toStdString();
[6488]594 satPhaseBias._time = _lastTime;
[6556]595 satPhaseBias._updateInt = _phaseBias.UpdateInterval;
[11047]596 satPhaseBias._ssrIOD = _phaseBias.SSRIOD;
597 satPhaseBias._ssrProviderID = _phaseBias.SSRProviderID;
[10998]598 satPhaseBias._ssrFormat = (_type == RTCMssr) ? ssrRtcmOld :
[10999]599 (_type == RTCMnewssr) ? ssrRtcmNew : ssrUnknown;
[11000]600 if (satPhaseBias._ssrFormat == e_ssrFormat::ssrRtcmNew) {
601 satPhaseBias._satYawInfoInd = _phaseBias.SatelliteYawInformationIndicator;
602 satPhaseBias._extPBPhaseInd = _phaseBias.ExtendedPhaseBiasPropertyID;
603 }
604 else {
605 satPhaseBias._dispBiasConsistInd = _phaseBias.DispersiveBiasConsistencyIndicator;
606 satPhaseBias._MelbWuebConsistInd = _phaseBias.MWConsistencyIndicator;
607 }
[8617]608 satPhaseBias._yaw = _phaseBias.Sat[ii].YawAngle;
609 satPhaseBias._yawRate = _phaseBias.Sat[ii].YawRate;
[6487]610 for (unsigned jj = 0; jj < _phaseBias.Sat[ii].NumberOfPhaseBiases; jj++) {
[9025]611 const SsrCorr::PhaseBias::PhaseBiasSat::PhaseBiasEntry& biasEntry = _phaseBias.Sat[ii].Biases[jj];
[6487]612 t_frqPhaseBias frqPhaseBias;
[10599]613 frqPhaseBias._rnxType2ch.assign(_ssrCorr->codeTypeToRnxType(sys, biasEntry.Type));
[11000]614 frqPhaseBias._value = biasEntry.Bias;
615 frqPhaseBias._fixIndicator = biasEntry.IntegerIndicator;
616 if (satPhaseBias._ssrFormat == e_ssrFormat::ssrRtcmNew) {
617 if (satPhaseBias._extPBPhaseInd) {
618 frqPhaseBias._fixWideLaneIndicator = biasEntry.WidelaneGroupIndicator;
619 }
620 }
621 else {
622 frqPhaseBias._fixWideLaneIndicator = biasEntry.WidelaneGroupIndicator;
623 }
624 frqPhaseBias._jumpCounter = biasEntry.DiscontinuityCounter;
[6487]625 if (!frqPhaseBias._rnxType2ch.empty()) {
626 satPhaseBias._bias.push_back(frqPhaseBias);
627 }
628 }
[7652]629 _phaseBiases[_lastTime].append(satPhaseBias);
[6487]630 }
631
[6490]632 // Ionospheric Model
633 // -----------------
634 if (_vTEC.NumLayers > 0) {
[6497]635 _vTecMap[_lastTime]._time = _lastTime;
[6556]636 _vTecMap[_lastTime]._updateInt = _vTEC.UpdateInterval;
[6564]637 _vTecMap[_lastTime]._staID = _staID.toStdString();
[6491]638 for (unsigned ii = 0; ii < _vTEC.NumLayers; ii++) {
[9025]639 const SsrCorr::VTEC::IonoLayers& ionoLayer = _vTEC.Layers[ii];
[6491]640 t_vTecLayer layer;
641 layer._height = ionoLayer.Height;
[6878]642 layer._C.ReSize(ionoLayer.Degree+1, ionoLayer.Order+1);
643 layer._S.ReSize(ionoLayer.Degree+1, ionoLayer.Order+1);
644 for (unsigned iDeg = 0; iDeg <= ionoLayer.Degree; iDeg++) {
645 for (unsigned iOrd = 0; iOrd <= ionoLayer.Order; iOrd++) {
[6491]646 layer._C[iDeg][iOrd] = ionoLayer.Cosinus[iDeg][iOrd];
647 layer._S[iDeg][iOrd] = ionoLayer.Sinus[iDeg][iOrd];
648 }
649 }
[6497]650 _vTecMap[_lastTime]._layers.push_back(layer);
[6491]651 }
[6490]652 }
653
[11008]654 // Metadata (model correction information)
655 // ----------------------------------------
656 if (_metaData.NumEntries > 0) {
657 // Metadata carries no epoch field of its own (it is keyed here by
658 // _lastTime, inherited from whichever other message last set it), so a
659 // fresh, complete message must replace rather than accumulate onto
660 // whatever a previous message at the same _lastTime already stored.
661 _metaDataMap[_lastTime] = t_metaData();
662 _metaDataMap[_lastTime]._time = _lastTime;
663 _metaDataMap[_lastTime]._staID = _staID.toStdString();
664 _metaDataMap[_lastTime]._ssrIOD = _metaData.SSRIOD;
665 _metaDataMap[_lastTime]._providerID = _metaData.SSRProviderID;
666 _metaDataMap[_lastTime]._solutionID = _metaData.SSRSolutionID;
667 for (unsigned ii = 0; ii < _metaData.NumEntries; ii++) {
668 const SsrCorr::MetaData::ModelPart& part = _metaData.Entries[ii];
669 t_metaDataEntry entry;
670 entry._typeIndicator = part.TypeIndicator;
671 entry._applicationIndicator = part.ApplicationIndicator;
672 entry._nonDefaultIndicator = part.nonDefaultIndicator;
673 entry._nonDefaultIdentifier = part.nonDefaultIdentifier;
674 entry._dataIODIndicator = part.DataIODIndicator;
675 entry._dataIOD = part.DataIOD;
676 _metaDataMap[_lastTime]._entries.push_back(entry);
677 }
678 }
679
[11038]680 // Satellite Antenna corrections
681 // ------------------------------
682 // _antenna, like _metaData, carries no epoch field of its own and is never
683 // wiped by reset(), so each system's data persists across unrelated
[11047]684 // messages. Only a system for which an Antenna message was actually
685 // received (_antennaReceived, latched in Decode()) is considered, so
686 // persisted data is never re-stamped with a newer time.
687 //
688 // The SatelliteAntennaIOD is unique only per SSR Provider ID (DF414) and
689 // only within 64 days, and a client that has not received the stream for
690 // 64 days must discard cached antenna data (enforced downstream against
691 // t_satAntenna::_time, see ssrSatAntennaTrusted() in pppSatObs.cpp). So an
692 // unchanged (provider ID, IOD) retransmission is suppressed only if it was
693 // last emitted less than ANT_REFRESH_SEC ago. Re-emitting it after that
694 // keeps _time close to the last reception - not the first - so continuously
695 // received data never ages out. It also means a reception after a long
696 // gap is always emitted, even when the provider has legitimately reused
697 // the IOD for different content, and each .ssr file contains the antenna
698 // data at least once per refresh interval.
699 //
700 // This is tracked per satellite, not per system: providers may split a
701 // system's satellites across several messages with rotating satellite
702 // masks (DF394), all carrying the same IOD - a per-system check would
703 // emit only whichever subset happened to arrive first.
[11038]704 {
[11047]705 const double ANT_REFRESH_SEC = 3600.0;
[11038]706 struct SysInfo { unsigned sysIdx; char sysChar; unsigned numSat; unsigned offset; };
707 const SysInfo sysInfos[] = {
708 { CLOCKORBIT_SATGPS, 'G', CLOCKORBIT_NUMGPS, CLOCKORBIT_OFFSETGPS },
709 { CLOCKORBIT_SATGLONASS, 'R', CLOCKORBIT_NUMGLONASS, CLOCKORBIT_OFFSETGLONASS },
710 { CLOCKORBIT_SATGALILEO, 'E', CLOCKORBIT_NUMGALILEO, CLOCKORBIT_OFFSETGALILEO },
711 { CLOCKORBIT_SATQZSS, 'J', CLOCKORBIT_NUMQZSS, CLOCKORBIT_OFFSETQZSS },
712 { CLOCKORBIT_SATBDS, 'C', CLOCKORBIT_NUMBDS, CLOCKORBIT_OFFSETBDS },
713 };
714 for (const SysInfo& si : sysInfos) {
715 unsigned s = si.sysIdx;
[11047]716 if (!_antennaReceived[s]) {
717 continue; // no Antenna message received for this system since the last check
[11038]718 }
[11047]719 _antennaReceived[s] = false;
[11038]720 if (_antenna.SatelliteMask[s] == 0) {
[11047]721 continue; // no satellites in this system's antenna data
[11038]722 }
723 for (unsigned k = 0; k < si.numSat; k++) {
724 if (!((_antenna.SatelliteMask[s] >> (63 - k)) & 1ULL)) {
725 continue;
726 }
[11047]727 unsigned i = si.offset + k;
728 if (_antennaSentTime[i].valid() &&
729 _antenna.SSRProviderID[s] == _antennaSentProviderID[i] &&
730 _antenna.SatelliteAntennaIOD[s] == _antennaSentIOD[i] &&
731 _lastTime - _antennaSentTime[i] < ANT_REFRESH_SEC) {
732 continue; // unchanged retransmission, emitted recently
733 }
734 _antennaSentProviderID[i] = _antenna.SSRProviderID[s];
735 _antennaSentIOD[i] = _antenna.SatelliteAntennaIOD[s];
736 _antennaSentTime[i] = _lastTime;
[11038]737 int num = k + 1; // PRN within this system, DF394 MSB-first convention
738 int flag = 0;
739 switch (si.sysChar) {
740 case 'G': flag = t_eph::LNAV; break;
741 case 'R': flag = t_eph::FDMA_M; break;
742 case 'E': flag = t_eph::INAV; break;
743 case 'J': flag = t_eph::LNAV; break;
744 case 'C': flag = (num < 6 || (num > 58 && num < 63)) ? t_eph::D2 : t_eph::D1; break;
745 }
746
747 t_satAntenna satAntenna;
748 satAntenna._prn.set(si.sysChar, num, flag);
749 satAntenna._staID = _staID.toStdString();
750 satAntenna._time = _lastTime;
751 satAntenna._ssrProviderID = _antenna.SSRProviderID[s];
752 satAntenna._satelliteAntennaIOD = _antenna.SatelliteAntennaIOD[s];
753 satAntenna._phaseCenterInfoInd = _antenna.PhaseCenterInformationIndicator[s];
754 satAntenna._groupDelayInfoInd = _antenna.GroupDelayInformationIndicator[s];
755 satAntenna._nadirAngleDependentCorrInd = _antenna.NadirAngleDependentCorrectionIndicator[s];
756 satAntenna._maximumOffNadirAngle = _antenna.maximumOffNadirAngle[s];
757 satAntenna._nadirAngleRangeExtension = _antenna.NadirAngleDependentCorrectionRangeExtension[s];
758
[11047]759 const SsrCorr::Antenna::SatellitePart& satPart = _antenna.Sat[i];
[11038]760 for (unsigned f = 0; f < ANT_MAXFREQUENCIES; f++) {
761 if (!((satPart.GnssFrequencyMask >> (ANT_MAXFREQUENCIES - 1 - f)) & 1U)) {
762 continue;
763 }
764 t_frqAntenna frqAntenna;
[11041]765 frqAntenna._frqType = _ssrCorr->antFreqIndexToStr(si.sysChar, f);
766 if (frqAntenna._frqType.empty()) {
767 continue; // reserved/unmapped DF+018 bit position
768 }
[11038]769 frqAntenna._nadirCorrectionIndicator = satPart.Freq[f].NadirCorrectionIndicator;
770 frqAntenna._nadirCorrection = satPart.Freq[f].NadirCorrection;
771 if (satAntenna._nadirAngleDependentCorrInd) {
772 unsigned count = satAntenna._maximumOffNadirAngle + 1;
773 for (unsigned deg = 0; deg < count && deg < ANT_MAXNADIRDEGREES; deg++) {
774 frqAntenna._nadirAngleCorrection.push_back(satPart.Freq[f].NadirAngleCorrection[deg]);
775 }
776 }
777 satAntenna._freq.push_back(frqAntenna);
778 }
779 _satAntennas[_lastTime].append(satAntenna);
780 }
781 }
782 }
783
[6455]784 // Dump all older epochs
785 // ---------------------
786 QMutableMapIterator<bncTime, QList<t_orbCorr> > itOrb(_orbCorrections);
787 while (itOrb.hasNext()) {
788 itOrb.next();
789 if (itOrb.key() < _lastTime) {
790 emit newOrbCorrections(itOrb.value());
[6456]791 t_orbCorr::writeEpoch(_out, itOrb.value());
[6455]792 itOrb.remove();
[7641]793 }
[6141]794 }
[6455]795 QMutableMapIterator<bncTime, QList<t_clkCorr> > itClk(_clkCorrections);
796 while (itClk.hasNext()) {
797 itClk.next();
798 if (itClk.key() < _lastTime) {
799 emit newClkCorrections(itClk.value());
[6456]800 t_clkCorr::writeEpoch(_out, itClk.value());
[6455]801 itClk.remove();
[7641]802 }
[6141]803 }
[6474]804 QMutableMapIterator<bncTime, QList<t_satCodeBias> > itCB(_codeBiases);
805 while (itCB.hasNext()) {
806 itCB.next();
807 if (itCB.key() < _lastTime) {
808 emit newCodeBiases(itCB.value());
[6475]809 t_satCodeBias::writeEpoch(_out, itCB.value());
[6474]810 itCB.remove();
[7641]811 }
[6474]812 }
[6487]813 QMutableMapIterator<bncTime, QList<t_satPhaseBias> > itPB(_phaseBiases);
814 while (itPB.hasNext()) {
815 itPB.next();
816 if (itPB.key() < _lastTime) {
817 emit newPhaseBiases(itPB.value());
818 t_satPhaseBias::writeEpoch(_out, itPB.value());
819 itPB.remove();
[7641]820 }
[6487]821 }
[6490]822 QMutableMapIterator<bncTime, t_vTec> itTec(_vTecMap);
823 while (itTec.hasNext()) {
824 itTec.next();
825 if (itTec.key() < _lastTime) {
826 emit newTec(itTec.value());
827 t_vTec::write(_out, itTec.value());
828 itTec.remove();
[7641]829 }
[6490]830 }
[11008]831 QMutableMapIterator<bncTime, t_metaData> itMD(_metaDataMap);
832 while (itMD.hasNext()) {
833 itMD.next();
834 if (itMD.key() < _lastTime) {
835 emit newMetaData(itMD.value());
836 t_metaData::write(_out, itMD.value());
837 itMD.remove();
838 }
839 }
[11038]840 QMutableMapIterator<bncTime, QList<t_satAntenna> > itAnt(_satAntennas);
841 while (itAnt.hasNext()) {
842 itAnt.next();
843 if (itAnt.key() < _lastTime) {
844 emit newSatAntennas(itAnt.value());
845 t_satAntenna::writeEpoch(_out, itAnt.value());
846 itAnt.remove();
847 }
848 }
[3022]849}
[5576]850
[6215]851//
[5576]852////////////////////////////////////////////////////////////////////////////
853void RTCM3coDecoder::checkProviderID() {
854
[10553]855 if (_clkOrb.SSRProviderID == 0 && _clkOrb.SSRSolutionID == 0 && _clkOrb.SSRIOD == 0) {
856 return;
857 }
858
[5576]859 int newProviderID[3];
[6454]860 newProviderID[0] = _clkOrb.SSRProviderID;
861 newProviderID[1] = _clkOrb.SSRSolutionID;
862 newProviderID[2] = _clkOrb.SSRIOD;
[10534]863 QString newProviderIDStr = QString(" [SSR Provider ID: %1 SSR Solution ID: %2 SSR IOD: %3]: ")
864 .arg(newProviderID[0]).arg(newProviderID[1]).arg(newProviderID[2]);
[5576]865
866 bool alreadySet = false;
867 bool different = false;
868
869 for (unsigned ii = 0; ii < 3; ii++) {
870 if (_providerID[ii] != -1) {
871 alreadySet = true;
872 }
873 if (_providerID[ii] != newProviderID[ii]) {
874 different = true;
875 }
876 _providerID[ii] = newProviderID[ii];
877 }
[6215]878
[5576]879 if (alreadySet && different) {
[10768]880 emit newMessage("RTCM3coDecoder: SSR Provider or Service changed " + newProviderIDStr.toLatin1() + _staID.toLatin1(), true);
[5577]881 emit providerIDChanged(_staID);
[5576]882 }
883}
[6467]884
[10572]885// Check corrections
886////////////////////////////////////////////////////////////////////////////
887bool RTCM3coDecoder::corrIsOutOfRange(const SsrCorr::ClockOrbit::SatData& coSat) {
888
[10617]889 QString ssrParStr;
890 QString ssrParValue;
891 bool corrIsOutOfRange = false;
[10572]892
[10617]893 switch (_type) {
894 // ======== //
895 // IGS SSR //
896 // ======== //
897 case IGSssr:
898 if (coSat.Clock.DeltaA0 < -209.7151 ||
899 coSat.Clock.DeltaA0 > +209.7151) {
900 ssrParStr = "Clock::DeltaA0";
901 ssrParValue = QString::number(coSat.Clock.DeltaA0, 'f', 4);
902 corrIsOutOfRange = true;
903 }
904 if (coSat.Clock.DeltaA1 < -1.048575 ||
905 coSat.Clock.DeltaA1 > +1.048575) {
906 ssrParStr = "Clock::DeltaA1";
907 ssrParValue = QString::number(coSat.Clock.DeltaA1, 'f', 6);
908 corrIsOutOfRange = true;
909 }
910 if (coSat.Clock.DeltaA2 < -1.3421772 ||
911 coSat.Clock.DeltaA2 > +1.3421772) {
912 ssrParStr = "Clock::DeltaA2";
913 ssrParValue = QString::number(coSat.Clock.DeltaA2, 'f', 7);
914 corrIsOutOfRange = true;
915 }
[10572]916
[10617]917 if (coSat.Orbit.DeltaRadial < -209.7151 ||
918 coSat.Orbit.DeltaRadial > +209.7151) {
919 ssrParStr = "Orbit::DeltaRadial";
920 ssrParValue = QString::number(coSat.Orbit.DeltaRadial, 'f', 4);
921 corrIsOutOfRange = true;
922 }
923
924 if (coSat.Orbit.DeltaAlongTrack < -209.7148 ||
925 coSat.Orbit.DeltaAlongTrack > +209.7148) {
926 ssrParStr = "Orbit::DeltaAlongTrack";
927 ssrParValue = QString::number(coSat.Orbit.DeltaAlongTrack, 'f', 4);
928 corrIsOutOfRange = true;
929 }
930 if (coSat.Orbit.DeltaCrossTrack < -209.7148 ||
931 coSat.Orbit.DeltaCrossTrack > +209.7148) {
932 ssrParStr = "Orbit::DeltaCrossTrack";
933 ssrParValue = QString::number(coSat.Orbit.DeltaCrossTrack, 'f', 4);
934 corrIsOutOfRange = true;
935 }
936
937 if (coSat.Orbit.DotDeltaRadial < -1.048575 ||
938 coSat.Orbit.DotDeltaRadial > +1.048575) {
939 ssrParStr = "Orbit::DotDeltaRadial";
940 ssrParValue = QString::number(coSat.Orbit.DotDeltaRadial, 'f', 6);
941 corrIsOutOfRange = true;
942 }
943 if (coSat.Orbit.DotDeltaAlongTrack < -1.048572 ||
944 coSat.Orbit.DotDeltaAlongTrack > +1.048572) {
945 ssrParStr = "Orbit::DotDeltaAlongTrack";
946 ssrParValue = QString::number(coSat.Orbit.DotDeltaAlongTrack, 'f', 6);
947 corrIsOutOfRange = true;
948 }
949 if (coSat.Orbit.DotDeltaCrossTrack < -1.048572 ||
950 coSat.Orbit.DotDeltaCrossTrack > +1.048572) {
951 ssrParStr = "Orbit::DotDeltaCrossTrack";
952 ssrParValue = QString::number(coSat.Orbit.DotDeltaCrossTrack, 'f', 6);
953 corrIsOutOfRange = true;
954 }
955 break;
956 //==========//
957 // RTCM SSR //
958 // =========//
959 case RTCMssr:
[10754]960 case RTCMnewssr:
[10617]961 if (coSat.Clock.DeltaA0 < -209.7151 ||
962 coSat.Clock.DeltaA0 > +209.7151) {
963 ssrParStr = "Clock::DeltaA0";
964 ssrParValue = QString::number(coSat.Clock.DeltaA0, 'f', 4);
965 corrIsOutOfRange = true;
966 }
967 if (coSat.Clock.DeltaA1 < -1.048575 ||
968 coSat.Clock.DeltaA1 > +1.048575) {
969 ssrParStr = "Clock::DeltaA1";
970 ssrParValue = QString::number(coSat.Clock.DeltaA1, 'f', 6);
971 corrIsOutOfRange = true;
972 }
973 if (coSat.Clock.DeltaA2 < -1.34217726 ||
974 coSat.Clock.DeltaA2 > +1.34217726) {
975 ssrParStr = "Clock::DeltaA2";
976 ssrParValue = QString::number(coSat.Clock.DeltaA2, 'f', 8);
977 corrIsOutOfRange = true;
978 }
979
980 if (coSat.Orbit.DeltaRadial < -209.7151 ||
981 coSat.Orbit.DeltaRadial > +209.7151) {
982 ssrParStr = "Orbit::DeltaRadial";
983 ssrParValue = QString::number(coSat.Orbit.DeltaRadial, 'f', 4);
984 corrIsOutOfRange = true;
985 }
986
987 if (coSat.Orbit.DeltaAlongTrack < -209.7148 ||
988 coSat.Orbit.DeltaAlongTrack > +209.7148) {
989 ssrParStr = "Orbit::DeltaAlongTrack";
990 ssrParValue = QString::number(coSat.Orbit.DeltaAlongTrack, 'f', 4);
991 corrIsOutOfRange = true;
992 }
993 if (coSat.Orbit.DeltaCrossTrack < -209.7148 ||
994 coSat.Orbit.DeltaCrossTrack > +209.7148) {
995 ssrParStr = "Orbit::DeltaCrossTrack";
996 ssrParValue = QString::number(coSat.Orbit.DeltaCrossTrack, 'f', 4);
997 corrIsOutOfRange = true;
998 }
999
1000 if (coSat.Orbit.DotDeltaRadial < -1.048575 ||
1001 coSat.Orbit.DotDeltaRadial > +1.048575) {
1002 ssrParStr = "Orbit::DotDeltaRadial";
1003 ssrParValue = QString::number(coSat.Orbit.DotDeltaRadial, 'f', 6);
1004 corrIsOutOfRange = true;
1005 }
1006 if (coSat.Orbit.DotDeltaAlongTrack < -1.048572 ||
1007 coSat.Orbit.DotDeltaAlongTrack > +1.048572) {
1008 ssrParStr = "Orbit::DotDeltaAlongTrack";
1009 ssrParValue = QString::number(coSat.Orbit.DotDeltaAlongTrack, 'f', 6);
1010 corrIsOutOfRange = true;
1011 }
1012 if (coSat.Orbit.DotDeltaCrossTrack < -1.048572 ||
1013 coSat.Orbit.DotDeltaCrossTrack > +1.048572) {
1014 ssrParStr = "Orbit::DotDeltaCrossTrack";
1015 ssrParValue = QString::number(coSat.Orbit.DotDeltaCrossTrack, 'f', 6);
1016 corrIsOutOfRange = true;
1017 }
1018 break;
1019 }
1020
1021 if (corrIsOutOfRange) {
1022 emit newMessage("RTCM3coDecoder: Correction " + ssrParStr.toLatin1()
1023 + " (" + ssrParValue.toLatin1() + ") "
1024 + "is out of range " + _staID.toLatin1(), true);
1025 }
1026
1027 return corrIsOutOfRange;
[10572]1028}
1029
[6467]1030//
1031////////////////////////////////////////////////////////////////////////////
[6556]1032void RTCM3coDecoder::setEpochTime() {
[6467]1033
[11010]1034 // _phaseBias is no longer wiped between messages (kept alive so a later
1035 // Extended Phase Bias message can correlate against it), so
1036 // NumberOfSat[s] > 0 alone no longer means "phase bias was just decoded
1037 // this cycle" - it can be stale from many messages ago. Gate each
1038 // phase-bias fallback below on the same per-system freshness tracking
1039 // sendResults() uses (_phaseBiasSentEpoch), so a stale leftover value
1040 // never gets picked as this cycle's epoch.
1041 bncTime prevTime = _lastTime;
[6467]1042 _lastTime.reset();
1043
[6854]1044 double epoSecGPS = -1.0;
1045 double epoSecGlo = -1.0;
1046 double epoSecGal = -1.0;
1047 double epoSecQzss = -1.0;
1048 double epoSecSbas = -1.0;
1049 double epoSecBds = -1.0;
[6467]1050 if (_clkOrb.NumberOfSat[CLOCKORBIT_SATGPS] > 0) {
[6553]1051 epoSecGPS = _clkOrb.EpochTime[CLOCKORBIT_SATGPS]; // 0 .. 604799 s
[6467]1052 }
1053 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATGPS] > 0) {
[7641]1054 epoSecGPS = _codeBias.EpochTime[CLOCKORBIT_SATGPS]; // 0 .. 604799 s
[6467]1055 }
[11010]1056 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGPS] > 0 &&
1057 _phaseBias.EpochTime[CLOCKORBIT_SATGPS] != _phaseBiasSentEpoch[CLOCKORBIT_SATGPS]) {
[7641]1058 epoSecGPS = _phaseBias.EpochTime[CLOCKORBIT_SATGPS]; // 0 .. 604799 s
[6470]1059 }
1060 else if (_vTEC.NumLayers > 0) {
[7641]1061 epoSecGPS = _vTEC.EpochTime; // 0 .. 604799 s
[6470]1062 }
[6467]1063 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATGLONASS] > 0) {
[6470]1064 epoSecGlo = _clkOrb.EpochTime[CLOCKORBIT_SATGLONASS]; // 0 .. 86399 s
[6467]1065 }
1066 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATGLONASS] > 0) {
[6470]1067 epoSecGlo = _codeBias.EpochTime[CLOCKORBIT_SATGLONASS]; // 0 .. 86399 s
[6467]1068 }
[11010]1069 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGLONASS] > 0 &&
1070 _phaseBias.EpochTime[CLOCKORBIT_SATGLONASS] != _phaseBiasSentEpoch[CLOCKORBIT_SATGLONASS]) {
[6470]1071 epoSecGlo = _phaseBias.EpochTime[CLOCKORBIT_SATGLONASS]; // 0 .. 86399 s
1072 }
[6854]1073 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATGALILEO] > 0) {
1074 epoSecGal = _clkOrb.EpochTime[CLOCKORBIT_SATGALILEO];
1075 }
1076 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATGALILEO] > 0) {
1077 epoSecGal = _codeBias.EpochTime[CLOCKORBIT_SATGALILEO];
1078 }
[11010]1079 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGALILEO] > 0 &&
1080 _phaseBias.EpochTime[CLOCKORBIT_SATGALILEO] != _phaseBiasSentEpoch[CLOCKORBIT_SATGALILEO]) {
[6854]1081 epoSecGal = _phaseBias.EpochTime[CLOCKORBIT_SATGALILEO];
1082 }
1083 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATQZSS] > 0) {
1084 epoSecQzss = _clkOrb.EpochTime[CLOCKORBIT_SATQZSS];
1085 }
1086 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATQZSS] > 0) {
1087 epoSecQzss = _codeBias.EpochTime[CLOCKORBIT_SATQZSS];
1088 }
[11010]1089 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATQZSS] > 0 &&
1090 _phaseBias.EpochTime[CLOCKORBIT_SATQZSS] != _phaseBiasSentEpoch[CLOCKORBIT_SATQZSS]) {
[6854]1091 epoSecQzss = _phaseBias.EpochTime[CLOCKORBIT_SATQZSS];
1092 }
1093 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATSBAS] > 0) {
1094 epoSecSbas = _clkOrb.EpochTime[CLOCKORBIT_SATSBAS];
1095 }
1096 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATSBAS] > 0) {
1097 epoSecSbas = _codeBias.EpochTime[CLOCKORBIT_SATSBAS];
1098 }
[11010]1099 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATSBAS] > 0 &&
1100 _phaseBias.EpochTime[CLOCKORBIT_SATSBAS] != _phaseBiasSentEpoch[CLOCKORBIT_SATSBAS]) {
[6854]1101 epoSecSbas = _phaseBias.EpochTime[CLOCKORBIT_SATSBAS];
1102 }
1103 else if (_clkOrb.NumberOfSat[CLOCKORBIT_SATBDS] > 0) {
1104 epoSecBds = _clkOrb.EpochTime[CLOCKORBIT_SATBDS];
1105 }
1106 else if (_codeBias.NumberOfSat[CLOCKORBIT_SATBDS] > 0) {
1107 epoSecBds = _codeBias.EpochTime[CLOCKORBIT_SATBDS];
1108 }
[11010]1109 else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATBDS] > 0 &&
1110 _phaseBias.EpochTime[CLOCKORBIT_SATBDS] != _phaseBiasSentEpoch[CLOCKORBIT_SATBDS]) {
[6854]1111 epoSecBds = _phaseBias.EpochTime[CLOCKORBIT_SATBDS];
1112 }
[6467]1113
1114 // Retrieve current time
1115 // ---------------------
1116 int currentWeek = 0;
1117 double currentSec = 0.0;
1118 currentGPSWeeks(currentWeek, currentSec);
1119 bncTime currentTime(currentWeek, currentSec);
1120
1121 // Set _lastTime close to currentTime
1122 // ----------------------------------
1123 if (epoSecGPS != -1) {
1124 _lastTime.set(currentWeek, epoSecGPS);
1125 }
1126 else if (epoSecGlo != -1) {
[10534]1127 QDate date = dateAndTimeFromGPSweek(currentTime.gpsw(), currentTime.gpssec()).date();
1128 if (_type == IGSssr) {
[10826]1129 epoSecGlo = epoSecGlo + gnumleap(date.year(), date.month(), date.day());
[10534]1130 }
[11011]1131 if (_type == RTCMssr || _type == RTCMnewssr) {
1132 // Same GLONASS epoch wire convention (UTC+3h) as RTCMssr
[9025]1133 epoSecGlo = epoSecGlo - 3 * 3600 + gnumleap(date.year(), date.month(), date.day());
1134 }
[6467]1135 _lastTime.set(currentWeek, epoSecGlo);
[6468]1136 }
[6854]1137 else if (epoSecGal != -1) {
1138 _lastTime.set(currentWeek, epoSecGal);
1139 }
1140 else if (epoSecQzss != -1) {
1141 _lastTime.set(currentWeek, epoSecQzss);
1142 }
1143 else if (epoSecSbas != -1) {
1144 _lastTime.set(currentWeek, epoSecSbas);
1145 }
1146 else if (epoSecBds != -1) {
[10826]1147 epoSecBds += 14.0;
1148 if (epoSecBds > 604800.0) {
1149 epoSecBds -= 7.0*24.0*60.0*60.0;
[7711]1150 }
[6854]1151 _lastTime.set(currentWeek, epoSecBds);
1152 }
[11011]1153 // Some message types (e.g. Metadata) carry no epoch field of their own,
1154 // and none of the above found anything fresh to use either - fall back
1155 // to the last known valid time rather than leaving _lastTime invalid
1156 // (which would silently drop the message's output entirely).
1157 else if (prevTime.valid()) {
1158 _lastTime = prevTime;
1159 }
[6468]1160
1161 if (_lastTime.valid()) {
[6469]1162 double maxDiff = 12 * 3600.0;
1163 while (_lastTime < currentTime - maxDiff) {
1164 _lastTime = _lastTime + maxDiff;
[6467]1165 }
[6469]1166 while (_lastTime > currentTime + maxDiff) {
1167 _lastTime = _lastTime - maxDiff;
[6467]1168 }
1169 }
1170}
[11047]1171
1172// Satellite numbers on the wire can exceed BNC's supported range (e.g. 6-bit
1173// IDs up to 63, while t_prn supports G01-G32). They are passed on unchanged,
1174// but t_prn::toInt() maps them to the unused index 0, so they are not usable
1175// for per-satellite processing - report that once per satellite.
1176////////////////////////////////////////////////////////////////////////////
1177void RTCM3coDecoder::checkPrnRange(const t_prn& prn) {
1178 if (prn.toInt() != 0) {
1179 return;
1180 }
1181 QString prnStr = QString::fromStdString(prn.toString());
1182 if (!_prnOutOfRangeLogged.contains(prnStr)) {
1183 _prnOutOfRangeLogged.insert(prnStr);
1184 emit newMessage(QString("%1: satellite %2 outside the range supported by BNC - not usable for processing")
1185 .arg(_staID).arg(prnStr).toLatin1(), true);
1186 }
1187}
1188
1189// All Satellite Antenna messages of one GNSS must use the same Satellite
1190// Antenna IOD (a satellite set may be split over several messages). During
1191// a legitimate IOD change old and new IOD are both seen for a while, until
1192// every message has been sent with the new one - but the IOD never returns
1193// to a replaced value within 64 days (uniqueness per SSR Provider ID), so a
1194// switch back to the replaced IOD within that period reveals messages of the
1195// same GNSS using different IODs. Reported once per GNSS and IOD pair; the
1196// data itself is not changed (PPP checks each satellite's IOD individually).
1197////////////////////////////////////////////////////////////////////////////
1198void RTCM3coDecoder::checkAntennaIOD(unsigned s) {
1199 const double MAX_AGE_SEC = 64.0 * 86400.0;
1200 unsigned int iod = _antenna.SatelliteAntennaIOD[s];
1201 unsigned int provider = _antenna.SSRProviderID[s];
1202
1203 int currentWeek = 0;
1204 double currentSec = 0.0;
1205 currentGPSWeeks(currentWeek, currentSec);
1206 bncTime currentTime(currentWeek, currentSec);
1207
1208 if (!_antennaIODKnown[s] || provider != _antennaIODProvider[s]) {
1209 _antennaIODKnown[s] = true;
1210 _antennaIODProvider[s] = provider;
1211 _antennaIODCur[s] = iod;
1212 _antennaIODPrevTime[s].reset(); // no replaced IOD yet
1213 return;
1214 }
1215 if (iod == _antennaIODCur[s]) {
1216 return;
1217 }
1218 if (_antennaIODPrevTime[s].valid() && iod == _antennaIODPrev[s] &&
1219 currentTime - _antennaIODPrevTime[s] < MAX_AGE_SEC) {
1220 const char sysChars[CLOCKORBIT_SATNUM] = {'G', 'R', 'E', 'J', 'S', 'C'};
1221 char sys = (s < CLOCKORBIT_SATNUM) ? sysChars[s] : '?';
1222 QString key = QString("%1_%2_%3").arg(sys).arg(_antennaIODCur[s]).arg(iod);
1223 if (!_antennaIODLogged.contains(key)) {
1224 _antennaIODLogged.insert(key);
1225 emit newMessage(QString("%1: GNSS %2 Satellite Antenna IOD switches back %3 -> %4"
1226 " - messages of one GNSS must use the same IOD")
1227 .arg(_staID).arg(sys).arg(_antennaIODCur[s]).arg(iod).toLatin1(), true);
1228 }
1229 }
1230 _antennaIODPrev[s] = _antennaIODCur[s];
1231 _antennaIODPrevTime[s] = currentTime;
1232 _antennaIODCur[s] = iod;
1233}
Note: See TracBrowser for help on using the repository browser.