source: ntrip/trunk/BNC/src/PPP/pppSatObs.cpp@ 11047

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

updates regarding RTCM-SSR and some ovarall improvements

  • Property svn:keywords set to Author Date Id Rev URL;svn:eol-style=native
  • Property svn:mime-type set to text/plain
File size: 35.4 KB
Line 
1/* -------------------------------------------------------------------------
2 * BKG NTRIP Client
3 * -------------------------------------------------------------------------
4 *
5 * Class: t_pppSatObs
6 *
7 * Purpose: Satellite observations
8 *
9 * Author: L. Mervart
10 *
11 * Created: 29-Jul-2014
12 *
13 * Changes:
14 *
15 * -----------------------------------------------------------------------*/
16
17
18#include <iostream>
19#include <iomanip>
20#include <cmath>
21#include <algorithm>
22#include <set>
23#include <newmatio.h>
24
25#include "pppSatObs.h"
26#include "bncconst.h"
27#include "pppEphPool.h"
28#include "pppStation.h"
29#include "bncutils.h"
30#include "bncantex.h"
31#include "pppObsPool.h"
32#include "pppClient.h"
33
34using namespace BNC_PPP;
35using namespace std;
36
37
38// Constructor
39////////////////////////////////////////////////////////////////////////////
40t_pppSatObs::t_pppSatObs(const t_satObs& pppSatObs) {
41 _prn = pppSatObs._prn;
42 _time = pppSatObs._time;
43 _outlier = false;
44 _valid = true;
45 _reference = false;
46 _stecSat = 0.0;
47 _stecRefSat = 0.0;
48 for (unsigned ii = 0; ii < t_frequency::max; ii++) {
49 _obs[ii] = 0;
50 }
51 prepareObs(pppSatObs);
52}
53
54// Destructor
55////////////////////////////////////////////////////////////////////////////
56t_pppSatObs::~t_pppSatObs() {
57 for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) {
58 delete _obs[iFreq];
59 }
60}
61
62//
63////////////////////////////////////////////////////////////////////////////
64bool t_pppSatObs::isBetter(const t_frqObs* aa, t_frqObs* bb, const string& trkModes) const {
65
66 if (!trkModes.empty()) {
67 size_t posA = trkModes.find(aa->trkChar());
68 size_t posB = trkModes.find(bb->trkChar());
69 if (posA != posB) {
70 if (posA == string::npos) {
71 return false;
72 }
73 else if (posB == string::npos) {
74 return true;
75 }
76 else {
77 return posA < posB;
78 }
79 }
80 }
81
82 unsigned numValA = 0;
83 if (aa->_codeValid) numValA += 1;
84 if (aa->_phaseValid) numValA += 1;
85
86 unsigned numValB = 0;
87 if (bb->_codeValid) numValB += 1;
88 if (bb->_phaseValid) numValB += 1;
89
90 if (numValA != numValB) {
91 return numValA > numValB;
92 }
93
94 return false;
95}
96
97//
98////////////////////////////////////////////////////////////////////////////
99void t_pppSatObs::prepareObs(const t_satObs& satObs) {
100
101 _model.reset();
102
103 const t_pppOptions::SysTrkModes* sysTrkModes = OPT->sysTrkModes(_prn.system());
104 using FrqTrkModes = t_pppOptions::SysTrkModes::FrqTrkModes;
105
106 for (const t_frqObs* obs : satObs._obs) {
107 if ( (obs->_codeValid || obs->_phaseValid)) {
108 t_frequency::type frq = t_frequency::toFreq(_prn.system(), obs->frqChar());
109 if (frq == t_frequency::dummy) {
110 continue;
111 }
112 string trkModes;
113 if (sysTrkModes) {
114 const vector<FrqTrkModes>& frqTrkModes = sysTrkModes->_frqTrkModes;
115 auto it = find_if(frqTrkModes.begin(), frqTrkModes.end(),
116 [frq](const FrqTrkModes& mm){return mm._frq == frq;});
117 if (it != frqTrkModes.end()) {
118 trkModes = it->_trkModes;
119 }
120 }
121 if (_obs[frq] == 0 || isBetter(obs, _obs[frq], trkModes)) {
122 delete _obs[frq];
123 _obs[frq] = new t_frqObs(*obs);
124 }
125 }
126 }
127
128 // Check whether all required frequencies available
129 // ------------------------------------------------
130 const std::vector<t_lc>& LCs = OPT->LCs(_prn.system());
131 for (unsigned ii = 0; ii < LCs.size(); ii++) {
132 t_lc LC = LCs[ii];
133 if (LC._type == t_lc::GIM) {
134 continue;
135 }
136 if (LC._frq1 != t_frequency::G5 && !isValid(LC)) {
137 _valid = false;
138 return;
139 }
140 }
141
142 // Find GLONASS Channel Number
143 // ---------------------------
144 if (_prn.system() == 'R') {
145 _channel = PPP_CLIENT->ephPool()->getChannel(_prn);
146 }
147 else {
148 _channel = 0;
149 }
150
151 // Compute Satellite Coordinates at Time of Transmission
152 // -----------------------------------------------------
153 _xcSat.ReSize(6); _xcSat = 0.0;
154 _vvSat.ReSize(3); _vvSat = 0.0;
155 bool totOK = false;
156 ColumnVector satPosOld(6); satPosOld = 0.0;
157
158 _valid = false;
159 t_frequency::type frq1 = t_frequency::dummy;
160 t_frequency::type frq2 = t_frequency::dummy;
161 OPT->defaultFrqs(_prn.system(), frq1, frq2);
162 if (frq1 != t_frequency::dummy) {
163 t_lc lc1(t_lc::code, frq1);
164 if (isValid(lc1)) {
165 _valid = true;
166 _rangeLC = lc1;
167 }
168 if (frq2 != t_frequency::dummy) {
169 t_lc lcIF(t_lc::codeIF, frq1, frq2);
170 if (isValid(lcIF)) {
171 _valid = true;
172 _rangeLC = lcIF;
173 }
174 }
175 }
176 if (!_valid) {
177 return;
178 }
179
180 double prange = obsValue(_rangeLC);
181 for (int ii = 1; ii <= 10; ii++) {
182 bncTime ToT = _time - prange / t_CST::c - _xcSat[3];
183 if (PPP_CLIENT->ephPool()->getCrd(_prn, ToT, _xcSat, _vvSat) != success) {
184 _valid = false;
185 return;
186 }
187 ColumnVector dx = _xcSat - satPosOld;
188 dx[3] *= t_CST::c;
189 if (dx.NormFrobenius() < 1.e-4) {
190 totOK = true;
191 break;
192 }
193 satPosOld = _xcSat;
194 }
195 if (totOK) {
196 _signalPropagationTime = prange / t_CST::c - _xcSat[3];
197 _model._satClkM = _xcSat[3] * t_CST::c;
198 }
199 else {
200 _valid = false;
201 }
202}
203
204//
205////////////////////////////////////////////////////////////////////////////
206void t_pppSatObs::lcCoeff(t_lc LC,
207 map<t_frequency::type, double>& codeCoeff,
208 map<t_frequency::type, double>& phaseCoeff,
209 map<t_frequency::type, double>& ionoCoeff) const {
210
211 codeCoeff.clear();
212 phaseCoeff.clear();
213 ionoCoeff.clear();
214
215 double f1 = t_CST::freq(LC._frq1, _channel);
216 double f2 = t_CST::freq(LC._frq2, _channel);
217 double f1GPS = t_CST::freq(t_frequency::G1, 0);
218
219 switch (LC._type) {
220 case t_lc::phase:
221 phaseCoeff[LC._frq1] = 1.0;
222 ionoCoeff [LC._frq1] = -1.0 * pow(f1GPS, 2) / pow(f1, 2);
223 return;
224 case t_lc::code:
225 codeCoeff[LC._frq1] = 1.0;
226 ionoCoeff[LC._frq1] = pow(f1GPS, 2) / pow(f1, 2);
227 return;
228 case t_lc::phaseIF:
229 phaseCoeff[LC._frq1] = f1 * f1 / (f1 * f1 - f2 * f2);
230 phaseCoeff[LC._frq2] = -f2 * f2 / (f1 * f1 - f2 * f2);
231 return;
232 case t_lc::codeIF:
233 codeCoeff[LC._frq1] = f1 * f1 / (f1 * f1 - f2 * f2);
234 codeCoeff[LC._frq2] = -f2 * f2 / (f1 * f1 - f2 * f2);
235 return;
236 case t_lc::MW:
237 phaseCoeff[LC._frq1] = f1 / (f1 - f2);
238 phaseCoeff[LC._frq2] = -f2 / (f1 - f2);
239 codeCoeff [LC._frq1] = -f1 / (f1 + f2);
240 codeCoeff [LC._frq2] = -f2 / (f1 + f2);
241 return;
242 case t_lc::CL:
243 phaseCoeff[LC._frq1] = 0.5;
244 codeCoeff [LC._frq1] = 0.5;
245 return;
246 case t_lc::GIM:
247 case t_lc::dummy:
248 case t_lc::maxLc:
249 return;
250 }
251}
252
253//
254////////////////////////////////////////////////////////////////////////////
255bool t_pppSatObs::isValid(t_lc LC) const {
256 bool valid = true;
257 obsValue(LC, &valid);
258
259 return valid;
260}
261//
262////////////////////////////////////////////////////////////////////////////
263double t_pppSatObs::obsValue(t_lc LC, bool* valid) const {
264
265 double retVal = 0.0;
266 if (valid) *valid = true;
267
268 // Pseudo observations (single-differenced: reference minus satellite)
269 if (LC._type == t_lc::GIM) {
270 if (_stecSat == 0.0 || _stecRefSat == 0.0) {
271 if (valid) *valid = false;
272 return 0.0;
273 }
274 else {
275 return _stecRefSat - _stecSat;
276 }
277 }
278
279 map<t_frequency::type, double> codeCoeff;
280 map<t_frequency::type, double> phaseCoeff;
281 map<t_frequency::type, double> ionoCoeff;
282 lcCoeff(LC, codeCoeff, phaseCoeff, ionoCoeff);
283
284 map<t_frequency::type, double>::const_iterator it;
285
286 // Code observations
287 for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
288 t_frequency::type tFreq = it->first;
289 if (_obs[tFreq] == 0) {
290 if (valid) *valid = false;
291 return 0.0;
292 }
293 else {
294 retVal += it->second * _obs[tFreq]->_code;
295 }
296 }
297 // Phase observations
298 for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) {
299 t_frequency::type tFreq = it->first;
300 if (_obs[tFreq] == 0) {
301 if (valid) *valid = false;
302 return 0.0;
303 }
304 else {
305 retVal += it->second * _obs[tFreq]->_phase * t_CST::lambda(tFreq, _channel);
306 }
307 }
308 return retVal;
309}
310
311//
312////////////////////////////////////////////////////////////////////////////
313double t_pppSatObs::lambda(t_lc LC) const {
314
315 double f1 = t_CST::freq(LC._frq1, _channel);
316 double f2 = t_CST::freq(LC._frq2, _channel);
317
318 if (LC._type == t_lc::phase) {
319 return t_CST::c / f1;
320 }
321 else if (LC._type == t_lc::phaseIF) {
322 return t_CST::c / (f1 + f2);
323 }
324 else if (LC._type == t_lc::MW) {
325 return t_CST::c / (f1 - f2);
326 }
327 else if (LC._type == t_lc::CL) {
328 return t_CST::c / f1 / 2.0;
329 }
330
331 return 0.0;
332}
333
334//
335////////////////////////////////////////////////////////////////////////////
336double t_pppSatObs::sigma(t_lc LC) const {
337
338 double retVal = 0.0;
339 map<t_frequency::type, double> codeCoeff;
340 map<t_frequency::type, double> phaseCoeff;
341 map<t_frequency::type, double> ionoCoeff;
342 lcCoeff(LC, codeCoeff, phaseCoeff, ionoCoeff);
343
344 if (LC._type == t_lc::GIM) {
345 retVal = OPT->_sigmaGIM * OPT->_sigmaGIM;
346 }
347
348 map<t_frequency::type, double>::const_iterator it;
349 for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
350 retVal += it->second * it->second * OPT->_sigmaC1 * OPT->_sigmaC1;
351 }
352
353 for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) {
354 retVal += it->second * it->second * OPT->_sigmaL1 * OPT->_sigmaL1;
355 }
356
357 retVal = sqrt(retVal);
358
359 // De-Weight R
360 // -----------
361 if (_prn.system() == 'R'&& LC.includesCode()) {
362 retVal *= 5.0;
363 }
364
365 // Elevation-Dependent Weighting
366 // -----------------------------
367 double cEle = 1.0;
368 if ( (OPT->_eleWgtCode && LC.includesCode()) ||
369 (OPT->_eleWgtPhase && LC.includesPhase()) ) {
370 double eleD = eleSat()*180.0/M_PI;
371 double hlp = fabs(90.0 - eleD);
372 cEle = (1.0 + hlp*hlp*hlp*0.000004);
373 }
374
375 return cEle * retVal;
376}
377
378//
379////////////////////////////////////////////////////////////////////////////
380double t_pppSatObs::maxRes(t_lc LC) const {
381 double retVal = 0.0;
382
383 map<t_frequency::type, double> codeCoeff;
384 map<t_frequency::type, double> phaseCoeff;
385 map<t_frequency::type, double> ionoCoeff;
386 lcCoeff(LC, codeCoeff, phaseCoeff, ionoCoeff);
387
388 map<t_frequency::type, double>::const_iterator it;
389 for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
390 retVal += it->second * it->second * OPT->_maxResC1 * OPT->_maxResC1;
391 }
392 for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) {
393 retVal += it->second * it->second * OPT->_maxResL1 * OPT->_maxResL1;
394 }
395
396 retVal = sqrt(retVal);
397
398 return retVal;
399}
400
401
402// Satellite Antenna IOD referenced by the SSR Metadata: the Data IOD (DF+069)
403// of the model-correction entry of type 1 (satellite antenna PCV) or type 2
404// (GDV). refIOD = 0 if Metadata references no antenna information (no
405// Metadata, no such entry, no Data IOD, or a zero Data IOD). A provider's
406// solution is relative to one Satellite Antenna information only, so two
407// different non-zero IODs are inconsistent: returns false in that case.
408//
409// A non-zero reference means the provider's biases, and orbits/clocks, are
410// relative to exactly that Satellite Antenna information, so the client
411// must apply it and nothing else (see t_pppSatObs::cmpModel()).
412////////////////////////////////////////////////////////////////////////////
413static bool ssrSatAntennaRefIOD(const t_metaData* metaData, unsigned int& refIOD) {
414 refIOD = 0;
415 if (!metaData) {
416 return true;
417 }
418 for (unsigned ii = 0; ii < metaData->_entries.size(); ii++) {
419 const t_metaDataEntry& entry = metaData->_entries[ii];
420 if ((entry._typeIndicator == 1 || entry._typeIndicator == 2) && // PCV or GDV
421 entry._dataIODIndicator && entry._dataIOD != 0) {
422 if (refIOD != 0 && refIOD != entry._dataIOD) {
423 return false;
424 }
425 refIOD = entry._dataIOD;
426 }
427 }
428 return true;
429}
430
431// Whether cached Satellite Antenna data is the information referenced by
432// Metadata (refIOD, from ssrSatAntennaRefIOD()). SatelliteAntennaIOD (DF+010)
433// must be non-zero and equal refIOD. Metadata carries this IOD once globally,
434// not per system (DF+010 is GNSS-specific, DF+069 is not), so the same
435// Metadata entries are reused for every system's check - the provider is
436// relied on to keep DF+010 synchronized with DF+069 across all systems.
437//
438// The IOD is also unique only per SSR Provider ID (DF414), so the Metadata
439// must come from the same provider as the Antenna message - antenna data may
440// be taken from the bias mountpoint while Metadata comes from the
441// corrections mountpoint (see pppRun.cpp).
442//
443// Separately, providers only guarantee SatelliteAntennaIOD uniqueness within
444// a rolling 64-day window (it wraps/repeats after that), so a client that
445// has been without a fresh Antenna message for that system for 64 days or
446// more must treat any cached data as unverifiable and stop using it, even
447// if the IOD check above would otherwise pass - the same IOD value could by
448// then legitimately mean something else. epoTime is the current processing
449// epoch, compared against t_satAntenna::_time (when this data was last
450// received, to within the decoder's re-emit interval - not a wire epoch,
451// the message carries none; see RTCM3coDecoder::sendResults()).
452////////////////////////////////////////////////////////////////////////////
453static bool ssrSatAntennaTrusted(const t_satAntenna* satAntenna, const t_metaData* metaData,
454 unsigned int refIOD, const bncTime& epoTime) {
455 const double MAX_AGE_SEC = 64.0 * 86400.0;
456 if (!satAntenna || !metaData || refIOD == 0 ||
457 satAntenna->_satelliteAntennaIOD != refIOD ||
458 satAntenna->_ssrProviderID != metaData->_providerID) {
459 return false;
460 }
461 if (epoTime.valid() && satAntenna->_time.valid() &&
462 epoTime - satAntenna->_time >= MAX_AGE_SEC) {
463 return false;
464 }
465 return true;
466}
467
468// Satellite antenna correction (offset + nadir-angle-dependent PCV) from a
469// live SSR Satellite Antenna message, in the same additive sense as
470// bncAntex::satCorr(). Returns false (corr untouched) if the data contains
471// no entry for this frequency.
472////////////////////////////////////////////////////////////////////////////
473static bool ssrSatAntennaCorr(const t_satAntenna* satAntenna, const string& frqStr,
474 double elTx, double& corr) {
475 if (!satAntenna) {
476 return false;
477 }
478 for (unsigned ii = 0; ii < satAntenna->_freq.size(); ii++) {
479 const t_frqAntenna& frq = satAntenna->_freq[ii];
480 if (frq._frqType != frqStr) {
481 continue;
482 }
483 corr = 0.0;
484 if (frq._nadirCorrectionIndicator) {
485 corr += frq._nadirCorrection;
486 }
487 if (!frq._nadirAngleCorrection.empty()) {
488 // 1-degree bins starting at nadir (0 deg), per RTCM-SSR spec
489 double nadirDeg = 90.0 - elTx * 180.0 / M_PI;
490 int idx = int(nadirDeg + 0.5);
491 if (idx < 0) {
492 idx = 0;
493 }
494 else if (idx >= (int)frq._nadirAngleCorrection.size()) {
495 idx = (int)frq._nadirAngleCorrection.size() - 1;
496 }
497 if (std::isnan(frq._nadirAngleCorrection[idx])) {
498 return false; // invalid value (DF+021 = -2^(n-1)) at this nadir angle
499 }
500 corr += frq._nadirAngleCorrection[idx];
501 }
502 return true;
503 }
504 return false;
505}
506
507//
508////////////////////////////////////////////////////////////////////////////
509t_irc t_pppSatObs::cmpModel(const t_pppStation* station) {
510
511 // Reset all model values
512 // ----------------------
513 _model.reset();
514
515 // Topocentric Satellite Position
516 // ------------------------------
517 ColumnVector rSat = _xcSat.Rows(1,3);
518 ColumnVector rRec = station->xyzApr();
519 ColumnVector rhoV = rSat - rRec;
520 _model._rho = rhoV.NormFrobenius();
521
522 ColumnVector vSat = _vvSat;
523
524 ColumnVector neu(3);
525 xyz2neu(station->ellApr().data(), rhoV.data(), neu.data());
526
527 _model._eleSat = acos(sqrt(neu[0]*neu[0] + neu[1]*neu[1]) / _model._rho);
528 if (neu[2] < 0.0) {
529 _model._eleSat *= -1.0;
530 }
531 _model._azSat = atan2(neu[1], neu[0]);
532
533 // Sun unit vector
534 ColumnVector xSun = t_astro::Sun(_time.mjddec());
535 xSun /= xSun.norm_Frobenius();
536
537 // Satellite unit vectors sz, sy, sx
538 ColumnVector sz = -rSat / rSat.norm_Frobenius();
539 ColumnVector sy = crossproduct(sz, xSun);
540 ColumnVector sx = crossproduct(sy, sz);
541
542 sx /= sx.norm_Frobenius();
543 sy /= sy.norm_Frobenius();
544
545 // LOS unit vector satellite --> receiver
546 ColumnVector rho = rRec - rSat;
547 rho /= rho.norm_Frobenius();
548
549 // LOS vector in satellite frame
550 ColumnVector u(3);
551 u(1) = dotproduct(sx, rho);
552 u(2) = dotproduct(sy, rho);
553 u(3) = dotproduct(sz, rho);
554
555 // Azimuth and elevation in satellite antenna frame
556 _model._elTx = atan2(u(3),sqrt(pow(u(2),2)+pow(u(1),2)));
557 _model._azTx = atan2(u(2),u(1));
558
559
560 // Satellite Clocks
561 // ----------------
562 _model._satClkM = _xcSat[3] * t_CST::c; // satellite system specific
563
564 // Receiver Clocks
565 // ---------------
566 _model._recClkM = station->dClk() * t_CST::c;
567
568 // Sagnac Effect (correction due to Earth rotation)
569 // ------------------------------------------------
570 ColumnVector Omega(3);
571 Omega[0] = 0.0;
572 Omega[1] = 0.0;
573 Omega[2] = t_CST::omega / t_CST::c;
574 _model._sagnac = DotProduct(Omega, crossproduct(rSat, rRec));
575
576 // Antenna Eccentricity
577 // --------------------
578 _model._antEcc = -DotProduct(station->xyzEcc(), rhoV) / _model._rho;
579
580 // Antenna Phase Center Offsets and Variations
581 // -------------------------------------------
582 // If SSR Metadata references Satellite Antenna information (non-zero IOD),
583 // the provider's biases are relative to exactly that information, so it
584 // is the only satellite antenna model applied - never mixed with ANTEX:
585 // phaseranges get the SSR correction if the Phase Center indicator
586 // (DF+011) is set, otherwise zero; pseudoranges likewise with the Group
587 // Delay indicator (DF+012). Zero values are part of that information
588 // (explicitly encoded, or DF+019/DF+013 not set); omitting a satellite or
589 // frequency within a constellation is not a valid way to signal zero. So
590 // if the referenced information is not (yet) available for this satellite,
591 // or lacks a frequency the PPP linear combinations need, the satellite is
592 // excluded rather than corrected with ANTEX. Without such a reference,
593 // the static ANTEX file is used for both observables.
594 if (PPP_CLIENT->antex()) {
595 const t_metaData* metaData = PPP_CLIENT->obsPool()->metaData();
596 const t_satAntenna* satAntenna = PPP_CLIENT->obsPool()->satAntenna(_prn);
597 unsigned int refIOD;
598 if (!ssrSatAntennaRefIOD(metaData, refIOD)) {
599 LOG << "SSR Metadata references different Satellite Antenna IODs (PCV/GDV) - "
600 << _prn.toString() << " excluded" << endl;
601 return failure;
602 }
603 bool useSsr = (refIOD != 0);
604 if (useSsr && !ssrSatAntennaTrusted(satAntenna, metaData, refIOD, _time)) {
605 LOG << "SSR Satellite Antenna IOD " << refIOD << " referenced by Metadata not available for "
606 << _prn.toString() << " - satellite excluded" << endl;
607 return failure;
608 }
609
610 bool phaseMissing[t_frequency::max] = {};
611 bool codeMissing[t_frequency::max] = {};
612
613 for (unsigned ii = 0; ii < t_frequency::max; ii++) {
614 t_frequency::type frqType = static_cast<t_frequency::type>(ii);
615 string frqStr = t_frequency::toString(frqType);
616 if (frqStr[0] != _prn.system()) {continue;}
617 bool found;
618 QString prn(_prn.toString().c_str());
619 _model._antPCO[ii] = PPP_CLIENT->antex()->rcvCorr(station->antName(), frqType, _model._eleSat, _model._azSat, found);
620
621 if (useSsr) {
622 // Unlike ANTEX's satCorr() (a single geometric PCO/PCV value shared
623 // by code and phase alike), DF+011/DF+012 say explicitly which
624 // observable(s) the correction applies to, so it is routed into the
625 // dedicated phase-only/code-only fields instead of the shared _antPCO.
626 // BDS 1207.14 MHz: B2 (BDS-2, "C7") or B2b (BDS-3, "C7b") - a
627 // satellite transmits only one of them. The entry of the satellite's
628 // own generation is preferred, taken from the ANTEX block type valid
629 // at this epoch (PRNs have been reassigned from BDS-2 to BDS-3).
630 // Since antenna corrections depend on the carrier, not the signal,
631 // the other entry is used as fallback if the own one is missing; if
632 // the generation is unknown, an entry is used only if unambiguous
633 // (just one present, or both equal).
634 // PRELIMINARY - pending clarification by RTCM SC-104 whether B2 and
635 // B2b entries of one satellite are meant to be identical; to revert
636 // to the strict selection, drop the fallback branches below.
637 double ssrCorr = 0.0;
638 bool ssrOK = false;
639 if (frqStr == "C7") {
640 double corrB2 = 0.0, corrB2b = 0.0;
641 bool hasB2 = ssrSatAntennaCorr(satAntenna, "C7", _model._elTx, corrB2);
642 bool hasB2b = ssrSatAntennaCorr(satAntenna, "C7b", _model._elTx, corrB2b);
643 QString blockType = PPP_CLIENT->antex()->satBlockType(prn, _time);
644 if (blockType.startsWith("3")) { // BDS-3: B2b, fallback B2
645 ssrOK = hasB2b || hasB2;
646 ssrCorr = hasB2b ? corrB2b : corrB2;
647 }
648 else if (blockType.startsWith("2")) { // BDS-2: B2, fallback B2b
649 ssrOK = hasB2 || hasB2b;
650 ssrCorr = hasB2 ? corrB2 : corrB2b;
651 }
652 else if (hasB2 != hasB2b || (hasB2 && corrB2 == corrB2b)) { // generation unknown
653 ssrOK = true;
654 ssrCorr = hasB2 ? corrB2 : corrB2b;
655 }
656 }
657 else {
658 ssrOK = ssrSatAntennaCorr(satAntenna, frqStr, _model._elTx, ssrCorr);
659 }
660 if (ssrOK) {
661 if (satAntenna->_phaseCenterInfoInd) {
662 _model._antPCV[ii] += ssrCorr;
663 }
664 if (satAntenna->_groupDelayInfoInd) {
665 _model._antGDV[ii] += ssrCorr;
666 }
667 }
668 else {
669 phaseMissing[ii] = satAntenna->_phaseCenterInfoInd;
670 codeMissing[ii] = satAntenna->_groupDelayInfoInd;
671 }
672 }
673 else {
674 _model._antPCO[ii] += PPP_CLIENT->antex()->satCorr(prn, frqType, _model._elTx, _model._azTx, found);
675 if (OPT->_isAPC && found) {
676 // the PCOs as given in the satellite antenna correction for all frequencies
677 // have to be reduced by the PCO of the respective reference frequency
678 if (_prn.system() == 'G') {
679 _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::G1, _model._elTx, _model._azTx, found);
680 }
681 else if (_prn.system() == 'R') {
682 _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::R1, _model._elTx, _model._azTx, found);
683 }
684 else if (_prn.system() == 'E') {
685 _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::E1, _model._elTx, _model._azTx, found);
686 }
687 else if (_prn.system() == 'C') {
688 _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::C2, _model._elTx, _model._azTx, found);
689 }
690 }
691 }
692 }
693
694 // If the SSR antenna information applies to both observables (both
695 // DF+011 and DF+012 set), the code and phase biases must come from the
696 // same SSR solution (same SSR IOD and Provider ID), otherwise they are
697 // not consistent with each other with respect to it. Only checked if
698 // phase biases are used and both IODs are known.
699 if (useSsr && satAntenna->_phaseCenterInfoInd && satAntenna->_groupDelayInfoInd &&
700 OPT->arSystem(_prn.system())) {
701 const t_satCodeBias* satCodeBias = PPP_CLIENT->obsPool()->satCodeBias(_prn);
702 const t_satPhaseBias* satPhaseBias = PPP_CLIENT->obsPool()->satPhaseBias(_prn);
703 if (satCodeBias && satPhaseBias &&
704 satCodeBias->_ssrIOD >= 0 && satPhaseBias->_ssrIOD >= 0 &&
705 (satCodeBias->_ssrIOD != satPhaseBias->_ssrIOD ||
706 satCodeBias->_ssrProviderID != satPhaseBias->_ssrProviderID)) {
707 LOG << "SSR code bias (IOD " << satCodeBias->_ssrIOD << ", provider " << satCodeBias->_ssrProviderID
708 << ") and phase bias (IOD " << satPhaseBias->_ssrIOD << ", provider " << satPhaseBias->_ssrProviderID
709 << ") inconsistent for " << _prn.toString() << " with shared SSR Satellite Antenna information"
710 << " - satellite excluded" << endl;
711 return failure;
712 }
713 }
714
715 // Exclude the satellite if the referenced antenna information omits a
716 // frequency actually used by the linear combinations of this system
717 const vector<t_lc>& LCs = OPT->LCs(_prn.system());
718 for (unsigned iLC = 0; iLC < LCs.size(); iLC++) {
719 map<t_frequency::type, double> codeCoeff;
720 map<t_frequency::type, double> phaseCoeff;
721 map<t_frequency::type, double> ionoCoeff;
722 lcCoeff(LCs[iLC], codeCoeff, phaseCoeff, ionoCoeff);
723 map<t_frequency::type, double>::const_iterator it;
724 for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) {
725 if (phaseMissing[it->first]) {
726 LOG << "SSR Satellite Antenna IOD " << refIOD << " has no valid entry for "
727 << _prn.toString() << ' ' << t_frequency::toString(it->first) << " (phase) - satellite excluded" << endl;
728 return failure;
729 }
730 }
731 for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
732 if (codeMissing[it->first]) {
733 LOG << "SSR Satellite Antenna IOD " << refIOD << " has no valid entry for "
734 << _prn.toString() << ' ' << t_frequency::toString(it->first) << " (code) - satellite excluded" << endl;
735 return failure;
736 }
737 }
738 }
739 }
740
741 // Tropospheric Delay
742 // ------------------
743 _model._tropo = t_tropo::delay_saast(rRec, _model._eleSat);
744
745 // Code Biases
746 // -----------
747 const t_satCodeBias* satCodeBias = PPP_CLIENT->obsPool()->satCodeBias(_prn);
748 if (satCodeBias) {
749 for (unsigned ii = 0; ii < satCodeBias->_bias.size(); ii++) {
750 const t_frqCodeBias& bias = satCodeBias->_bias[ii];
751 for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) {
752 string frqStr = t_frequency::toString(t_frequency::type(iFreq));
753 if (frqStr[0] != _prn.system()) {
754 continue;
755 }
756 const t_frqObs* obs = _obs[iFreq];
757 if (obs && obs->_rnxType2ch == bias._rnxType2ch) {
758 _model._codeBias[iFreq] = (bias._value != 0.0 ? bias._value : ZEROVALUE);
759 }
760 }
761 }
762 }
763
764 // Phase Biases
765 // -----------
766 double yaw = 0.0;
767 bool useYaw = false;
768 if (OPT->arSystem(_prn.system())) {
769 const t_satPhaseBias* satPhaseBias = PPP_CLIENT->obsPool()->satPhaseBias(_prn);
770 if (satPhaseBias) {
771 if (OPT->_ar._useYaw) {
772 double dt = station->epochTime() - satPhaseBias->_time;
773 if (satPhaseBias->_updateInt) {
774 dt -= (0.5 * ssrUpdateInt[satPhaseBias->_updateInt]);
775 }
776 yaw = satPhaseBias->_yaw + satPhaseBias->_yawRate * dt;
777 useYaw = true;
778 }
779 for (unsigned ii = 0; ii < satPhaseBias->_bias.size(); ii++) {
780 const t_frqPhaseBias& bias = satPhaseBias->_bias[ii];
781 if (bias._fixIndicator) { // if AR, biases without fixIndicator not used
782 for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) {
783 string frqStr = t_frequency::toString(t_frequency::type(iFreq));
784 if (frqStr[0] != _prn.system()) {
785 continue;
786 }
787 t_frqObs* obs = _obs[iFreq];
788 if (obs && obs->_rnxType2ch[0] == bias._rnxType2ch[0]) { // allow different tracking mode
789 _model._phaseBias[iFreq] = (bias._value != 0.0 ? bias._value : ZEROVALUE);
790 obs->_biasJumpCounter = bias._jumpCounter;
791 }
792 }
793 }
794 }
795 }
796 }
797
798 // Phase Wind-Up
799 // -------------
800 _model._windUp = station->windUp(_time, _prn, rSat, useYaw, yaw, vSat) ;
801
802 // Relativistic effect due to earth gravity
803 // ----------------------------------------
804 double a = rSat.NormFrobenius() + rRec.NormFrobenius();
805 double b = (rSat - rRec).NormFrobenius();
806 double gm = 3.986004418e14; // m3/s2
807 _model._rel = 2 * gm / t_CST::c / t_CST::c * log((a + b) / (a - b));
808
809 // Tidal Correction
810 // ----------------
811 _model._tideEarth = -DotProduct(station->tideDsplEarth(), rhoV) / _model._rho;
812 _model._tideOcean = -DotProduct(station->tideDsplOcean(), rhoV) / _model._rho;
813
814 // Ionospheric Delay
815 // -----------------
816 const t_vTec* vTec = PPP_CLIENT->obsPool()->vTec();
817 bool vTecUsage = true;
818 for (unsigned ii = 0; ii < OPT->LCs(_prn.system()).size(); ii++) {
819 t_lc LC = OPT->LCs(_prn.system())[ii];
820 if (LC._type == t_lc::codeIF || LC._type == t_lc::phaseIF) {
821 vTecUsage = false;
822 }
823 }
824
825 if (vTecUsage && vTec) {
826 double stec = station->stec(vTec, _signalPropagationTime, rSat);
827 double f1GPS = t_CST::freq(t_frequency::G1, 0);
828 for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) {
829 if (OPT->_pseudoObsIono) {
830 // For scaling the slant ionospheric delays the trick is to be consistent with units!
831 // The conversion of TECU into meters requires the frequency of the signal.
832 // Hence, GPS L1 frequency is used for all systems. The same is true for mu_i in lcCoeff().
833 _model._ionoCodeDelay[iFreq] = 40.3E16 / pow(f1GPS, 2) * stec;
834 }
835 else { // PPP-RTK
836 t_frequency::type frqType = static_cast<t_frequency::type>(iFreq);
837 _model._ionoCodeDelay[iFreq] = 40.3E16 / pow(t_CST::freq(frqType, _channel), 2) * stec;
838 }
839 }
840 }
841
842 // Set Model Set Flag
843 // ------------------
844 _model._set = true;
845
846 if (OPT->_logMode == t_pppOptions::all) {
847 printModel();
848 }
849
850 return success;
851}
852
853//
854////////////////////////////////////////////////////////////////////////////
855void t_pppSatObs::printModel() const {
856
857 LOG.setf(ios::fixed);
858 LOG << "\nMODEL for Satellite " << _prn.toString() << (isReference() ? " (Reference Satellite)" : "")
859
860 << "\n======================= " << endl
861 << "PPP "
862 << ((OPT->_pseudoObsIono) ? " with pseudo-observations for STEC" : "") << endl
863 << "RHO : " << setw(12) << setprecision(3) << _model._rho << endl
864 << "ELE : " << setw(12) << setprecision(3) << _model._eleSat * RHO_DEG << endl
865 << "AZI : " << setw(12) << setprecision(3) << _model._azSat * RHO_DEG << endl
866 << "SATCLK : " << setw(12) << setprecision(3) << _model._satClkM << endl
867 << "RECCLK : " << setw(12) << setprecision(3) << _model._recClkM << endl
868 << "SAGNAC : " << setw(12) << setprecision(3) << _model._sagnac << endl
869 << "ANTECC : " << setw(12) << setprecision(3) << _model._antEcc << endl
870 << "TROPO : " << setw(12) << setprecision(3) << _model._tropo << endl
871 << "WINDUP : " << setw(12) << setprecision(3) << _model._windUp << endl
872 << "REL : " << setw(12) << setprecision(3) << _model._rel << endl
873 << "EARTH TIDES : " << setw(12) << setprecision(3) << _model._tideEarth << endl
874 << "OCEAN TIDES : " << setw(12) << setprecision(3) << _model._tideOcean << endl
875 << endl
876 << "FREQUENCY DEPENDENT CORRECTIONS:" << endl
877 << "-------------------------------" << endl;
878 for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) {
879 if (_obs[iFreq]) {
880 string frqStr = t_frequency::toString(t_frequency::type(iFreq));
881 if (_prn.system() == frqStr[0]) {
882 LOG << "PCO : " << frqStr << setw(12) << setprecision(3) << _model._antPCO[iFreq] << endl
883 << "SSR PCV : " << frqStr << setw(12) << setprecision(3) << _model._antPCV[iFreq] << endl
884 << "SSR GDV : " << frqStr << setw(12) << setprecision(3) << _model._antGDV[iFreq] << endl
885 << "BIAS CODE : " << frqStr << setw(12) << setprecision(3) << _model._codeBias[iFreq] << "\t(" << _obs[iFreq]->trkChar() << ") " << endl
886 << "BIAS PHASE : " << frqStr << setw(12) << setprecision(3) << _model._phaseBias[iFreq] << "\t(" << _obs[iFreq]->trkChar() << ") " << endl
887 << "IONO CODEDELAY: " << frqStr << setw(12) << setprecision(3) << _model._ionoCodeDelay[iFreq]<< endl;
888 }
889 }
890 }
891}
892
893//
894////////////////////////////////////////////////////////////////////////////
895void t_pppSatObs::printObsMinusComputed() const {
896 LOG.setf(ios::fixed);
897 LOG << "\nOBS-COMP for Satellite " << _prn.toString() << (isReference() ? " (Reference Satellite)" : "") << endl
898 << "========================== " << endl;
899 char sys = _prn.system();
900 for (unsigned ii = 0; ii < OPT->LCs(sys).size(); ii++) {
901 t_lc LC = OPT->LCs(sys)[ii];
902 LOG << "OBS-CMP " << setw(4) << LC.toString() << ": " << _prn.toString() << " "
903 << setw(12) << setprecision(3) << obsValue(LC) << " "
904 << setw(12) << setprecision(3) << cmpValue(LC) << " "
905 << setw(12) << setprecision(3) << obsValue(LC) - cmpValue(LC) << endl;
906 }
907}
908
909//
910////////////////////////////////////////////////////////////////////////////
911double t_pppSatObs::cmpValueForBanc(t_lc LC) const {
912 return cmpValue(LC) - _model._rho - _model._sagnac - _model._recClkM;
913}
914
915//
916////////////////////////////////////////////////////////////////////////////
917double t_pppSatObs::cmpValue(t_lc LC) const {
918 double cmpValue;
919
920 if (!isValid(LC)) {
921 cmpValue = 0.0;
922 }
923 else if (LC._type == t_lc::GIM) {
924 cmpValue = 0.0;
925 }
926 else {
927 // Non-Dispersive Part
928 // -------------------
929 double nonDisp = _model._rho
930 + _model._recClkM - _model._satClkM
931 + _model._sagnac + _model._antEcc + _model._tropo
932 + _model._tideEarth + _model._tideOcean + _model._rel;
933
934 // Add Dispersive Part
935 // -------------------
936 double dispPart = 0.0;
937 map<t_frequency::type, double> codeCoeff;
938 map<t_frequency::type, double> phaseCoeff;
939 map<t_frequency::type, double> ionoCoeff;
940 lcCoeff(LC, codeCoeff, phaseCoeff, ionoCoeff);
941 map<t_frequency::type, double>::const_iterator it;
942 for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
943 t_frequency::type tFreq = it->first;
944 dispPart += it->second * (_model._antPCO[tFreq] + _model._antGDV[tFreq] - _model._codeBias[tFreq]);
945 }
946 for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) {
947 t_frequency::type tFreq = it->first;
948 dispPart += it->second * (_model._antPCO[tFreq] + _model._antPCV[tFreq] - _model._phaseBias[tFreq] +
949 _model._windUp * t_CST::lambda(tFreq, _channel));
950 }
951 cmpValue = nonDisp + dispPart;
952 }
953
954 return cmpValue;
955}
956
957//
958////////////////////////////////////////////////////////////////////////////
959void t_pppSatObs::setRes(t_lc LC, double res) {
960 _res[LC] = res;
961}
962
963//
964////////////////////////////////////////////////////////////////////////////
965double t_pppSatObs::getRes(t_lc LC) const {
966 map<t_lc, double>::const_iterator it = _res.find(LC);
967 if (it != _res.end()) {
968 return it->second;
969 }
970 else {
971 return 0.0;
972 }
973}
974
975//
976////////////////////////////////////////////////////////////////////////////
977bool t_pppSatObs::setPseudoObsIono(t_frequency::type freq, double stecRefSat) {
978 _stecSat = _model._ionoCodeDelay[freq];
979 _stecRefSat = stecRefSat;
980 return (_stecSat != 0.0 && _stecRefSat != 0.0);
981}
982
983//
984////////////////////////////////////////////////////////////////////////////
985bool t_pppSatObs::hasBiases() const {
986 bool ar = OPT->arSystem(_prn.system());
987 set<t_frequency::type> frqs;
988 for (const auto& lc : OPT->LCs(_prn.system())) {
989 if (lc._frq1 != t_frequency::dummy) frqs.insert(lc._frq1);
990 if (lc._frq2 != t_frequency::dummy) frqs.insert(lc._frq2);
991 }
992 for (int iFreq : frqs) {
993 if (_obs[iFreq] != 0) {
994 if (_model._codeBias[iFreq] == 0 || (ar && _model._phaseBias[iFreq] == 0)) {
995 return false;
996 }
997 }
998 }
999 return true;
1000}
Note: See TracBrowser for help on using the repository browser.