/* ------------------------------------------------------------------------- * BKG NTRIP Client * ------------------------------------------------------------------------- * * Class: t_pppSatObs * * Purpose: Satellite observations * * Author: L. Mervart * * Created: 29-Jul-2014 * * Changes: * * -----------------------------------------------------------------------*/ #include #include #include #include "pppSatObs.h" #include "bncconst.h" #include "pppEphPool.h" #include "pppStation.h" #include "bncutils.h" #include "bncantex.h" #include "pppObsPool.h" #include "pppClient.h" using namespace BNC_PPP; using namespace std; // Constructor //////////////////////////////////////////////////////////////////////////// t_pppSatObs::t_pppSatObs(const t_satObs& pppSatObs) { _prn = pppSatObs._prn; _time = pppSatObs._time; _outlier = false; _valid = true; for (unsigned ii = 0; ii < t_frequency::max; ii++) { _obs[ii] = 0; } prepareObs(pppSatObs); } // Destructor //////////////////////////////////////////////////////////////////////////// t_pppSatObs::~t_pppSatObs() { for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) { delete _obs[iFreq]; } } // //////////////////////////////////////////////////////////////////////////// void t_pppSatObs::prepareObs(const t_satObs& pppSatObs) { _model.reset(); // Select pseudoranges and phase observations // ------------------------------------------ const string preferredAttrib = "CWPXI_"; for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) { string frqNum = t_frequency::toString(t_frequency::type(iFreq)).substr(1); for (unsigned iPref = 0; iPref < preferredAttrib.length(); iPref++) { string obsType = (preferredAttrib[iPref] == '_') ? frqNum : frqNum + preferredAttrib[iPref]; if (_obs[iFreq] == 0) { for (unsigned ii = 0; ii < pppSatObs._obs.size(); ii++) { const t_frqObs* obs = pppSatObs._obs[ii]; if (obs->_rnxType2ch == obsType && obs->_codeValid && obs->_code && obs->_phaseValid && obs->_phase) { _obs[iFreq] = new t_frqObs(*obs); } } } } } // Used frequency types // -------------------- _fType1 = t_lc::toFreq(_prn.system(),t_lc::l1); _fType2 = t_lc::toFreq(_prn.system(),t_lc::l2); // Check whether all required frequencies available // ------------------------------------------------ for (unsigned ii = 0; ii < OPT->LCs(_prn.system()).size(); ii++) { t_lc::type tLC = OPT->LCs(_prn.system())[ii]; if (!isValid(tLC)) { _valid = false; return; } } // Find Glonass Channel Number // --------------------------- if (_prn.system() == 'R') { _channel = PPP_CLIENT->ephPool()->getChannel(_prn); } else { _channel = 0; } // Compute Satellite Coordinates at Time of Transmission // ----------------------------------------------------- _xcSat.ReSize(4); _xcSat = 0.0; _vvSat.ReSize(4); _vvSat = 0.0; bool totOK = false; ColumnVector satPosOld(4); satPosOld = 0.0; t_lc::type tLC = isValid(t_lc::cIF) ? t_lc::cIF : t_lc::c1; double prange = obsValue(tLC); for (int ii = 1; ii <= 10; ii++) { bncTime ToT = _time - prange / t_CST::c - _xcSat[3]; if (PPP_CLIENT->ephPool()->getCrd(_prn, ToT, _xcSat, _vvSat) != success) { _valid = false; return; } ColumnVector dx = _xcSat - satPosOld; dx[3] *= t_CST::c; if (dx.norm_Frobenius() < 1.e-4) { totOK = true; break; } satPosOld = _xcSat; } if (totOK) { _signalPropagationTime = prange / t_CST::c - _xcSat[3]; _model._satClkM = _xcSat[3] * t_CST::c; } else { _valid = false; } } // //////////////////////////////////////////////////////////////////////////// void t_pppSatObs::lcCoeff(t_lc::type tLC, map& codeCoeff, map& phaseCoeff) const { codeCoeff.clear(); phaseCoeff.clear(); double f1 = t_CST::freq(_fType1, _channel); double f2 = t_CST::freq(_fType2, _channel); switch (tLC) { case t_lc::l1: phaseCoeff[_fType1] = 1.0; return; case t_lc::l2: phaseCoeff[_fType2] = 1.0; return; case t_lc::lIF: phaseCoeff[_fType1] = f1 * f1 / (f1 * f1 - f2 * f2); phaseCoeff[_fType2] = -f2 * f2 / (f1 * f1 - f2 * f2); return; case t_lc::MW: phaseCoeff[_fType1] = f1 / (f1 - f2); phaseCoeff[_fType2] = -f2 / (f1 - f2); codeCoeff[_fType1] = -f1 / (f1 + f2); codeCoeff[_fType2] = -f2 / (f1 + f2); return; case t_lc::CL: phaseCoeff[_fType1] = 0.5; codeCoeff[_fType1] = 0.5; return; case t_lc::c1: codeCoeff[_fType1] = 1.0; return; case t_lc::c2: codeCoeff[_fType2] = 1.0; return; case t_lc::cIF: codeCoeff[_fType1] = f1 * f1 / (f1 * f1 - f2 * f2); codeCoeff[_fType2] = -f2 * f2 / (f1 * f1 - f2 * f2); return; case t_lc::dummy: case t_lc::maxLc: return; } } // //////////////////////////////////////////////////////////////////////////// bool t_pppSatObs::isValid(t_lc::type tLC) const { bool valid = true; obsValue(tLC, &valid); return valid; } // //////////////////////////////////////////////////////////////////////////// double t_pppSatObs::obsValue(t_lc::type tLC, bool* valid) const { map codeCoeff; map phaseCoeff; lcCoeff(tLC, codeCoeff, phaseCoeff); double retVal = 0.0; if (valid) *valid = true; map::const_iterator it; for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) { t_frequency::type tFreq = it->first; if (_obs[tFreq] == 0) { if (valid) *valid = false; return 0.0; } else { retVal += it->second * _obs[tFreq]->_code; } } for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) { t_frequency::type tFreq = it->first; if (_obs[tFreq] == 0) { if (valid) *valid = false; return 0.0; } else { retVal += it->second * _obs[tFreq]->_phase * t_CST::lambda(tFreq, _channel); } } return retVal; } // //////////////////////////////////////////////////////////////////////////// double t_pppSatObs::lambda(t_lc::type tLC) const { double f1 = t_CST::freq(_fType1, _channel); double f2 = t_CST::freq(_fType2, _channel); if (tLC == t_lc::l1) { return t_CST::c / f1; } else if (tLC == t_lc::l2) { return t_CST::c / f2; } else if (tLC == t_lc::lIF) { return t_CST::c / (f1 + f2); } else if (tLC == t_lc::MW) { return t_CST::c / (f1 - f2); } else if (tLC == t_lc::CL) { return t_CST::c / f1 / 2.0; } return 0.0; } // //////////////////////////////////////////////////////////////////////////// double t_pppSatObs::sigma(t_lc::type tLC) const { map codeCoeff; map phaseCoeff; lcCoeff(tLC, codeCoeff, phaseCoeff); double retVal = 0.0; map::const_iterator it; for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) { retVal += it->second * it->second * OPT->_sigmaC1 * OPT->_sigmaC1; } for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) { retVal += it->second * it->second * OPT->_sigmaL1 * OPT->_sigmaL1; } retVal = sqrt(retVal); // De-Weight GLONASS // ----------------- if (_prn.system() == 'R') { retVal *= 5.0; } // Elevation-Dependent Weighting // ----------------------------- double cEle = 1.0; if ( (OPT->_eleWgtCode && t_lc::includesCode(tLC)) || (OPT->_eleWgtPhase && t_lc::includesPhase(tLC)) ) { double eleD = eleSat()*180.0/M_PI; double hlp = fabs(90.0 - eleD); cEle = (1.0 + hlp*hlp*hlp*0.000004); } return cEle * retVal; } // //////////////////////////////////////////////////////////////////////////// double t_pppSatObs::maxRes(t_lc::type tLC) const { map codeCoeff; map phaseCoeff; lcCoeff(tLC, codeCoeff, phaseCoeff); double retVal = 0.0; map::const_iterator it; for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) { retVal += it->second * it->second * OPT->_maxResC1 * OPT->_maxResC1; } for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) { retVal += it->second * it->second * OPT->_maxResL1 * OPT->_maxResL1; } return sqrt(retVal); } // //////////////////////////////////////////////////////////////////////////// t_irc t_pppSatObs::cmpModel(const t_pppStation* station) { // Reset all model values // ---------------------- _model.reset(); // Topocentric Satellite Position // ------------------------------ ColumnVector rSat = _xcSat.Rows(1,3); ColumnVector rhoV = rSat - station->xyzApr(); _model._rho = rhoV.norm_Frobenius(); ColumnVector neu(3); xyz2neu(station->ellApr().data(), rhoV.data(), neu.data()); _model._eleSat = acos( sqrt(neu[0]*neu[0] + neu[1]*neu[1]) / _model._rho ); if (neu[2] < 0) { _model._eleSat *= -1.0; } _model._azSat = atan2(neu[1], neu[0]); // Satellite Clocks // ---------------- _model._satClkM = _xcSat[3] * t_CST::c; // Receiver Clocks // --------------- _model._recClkM = station->dClk() * t_CST::c; // Sagnac Effect (correction due to Earth rotation) // ------------------------------------------------ ColumnVector Omega(3); Omega[0] = 0.0; Omega[1] = 0.0; Omega[2] = t_CST::omega / t_CST::c; _model._sagnac = DotProduct(Omega, crossproduct(rSat, station->xyzApr())); // Antenna Eccentricity // -------------------- _model._antEcc = -DotProduct(station->xyzEcc(), rhoV) / _model._rho; // Antenna Phase Center Offsets and Variations // ------------------------------------------- if (PPP_CLIENT->antex()) { for (unsigned ii = 0; ii < t_frequency::max; ii++) { t_frequency::type frqType = static_cast(ii); bool found; _model._antPCO[ii] = PPP_CLIENT->antex()->rcvCorr(station->antName(), frqType, _model._eleSat, _model._azSat, found); } } // Tropospheric Delay // ------------------ _model._tropo = t_tropo::delay_saast(station->xyzApr(), _model._eleSat); // Phase Wind-Up // ------------- _model._windUp = station->windUp(_time, _prn, rSat); // Code Biases // ----------- const t_satCodeBias* satCodeBias = PPP_CLIENT->obsPool()->satCodeBias(_prn); if (satCodeBias) { for (unsigned ii = 0; ii < satCodeBias->_bias.size(); ii++) { const t_frqCodeBias& bias = satCodeBias->_bias[ii]; for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) { const t_frqObs* obs = _obs[iFreq]; if (obs && obs->_rnxType2ch == bias._rnxType2ch) { _model._codeBias[iFreq] = bias._value; } } } } // Phase Biases // ----------- // TODO: consideration of fix indicators, yaw angle and jump counter const t_satPhaseBias* satPhaseBias = PPP_CLIENT->obsPool()->satPhaseBias(_prn); if (satPhaseBias) { for (unsigned ii = 0; ii < satPhaseBias->_bias.size(); ii++) { const t_frqPhaseBias& bias = satPhaseBias->_bias[ii]; for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) { const t_frqObs* obs = _obs[iFreq]; if (obs && obs->_rnxType2ch == bias._rnxType2ch) { _model._phaseBias[iFreq] = bias._value; } } } } // Tidal Correction // ---------------- _model._tide = -DotProduct(station->tideDspl(), rhoV) / _model._rho; // Ionospheric Delay // ----------------- const t_vTec* vTec = PPP_CLIENT->obsPool()->vTec(); bool vTecUsage = true; for (unsigned ii = 0; ii < OPT->LCs(_prn.system()).size(); ii++) { t_lc::type tLC = OPT->LCs(_prn.system())[ii]; if (tLC == t_lc::cIF || tLC == t_lc::lIF) { vTecUsage = false; } } if (vTecUsage && vTec) { double stec = station->stec(vTec, _signalPropagationTime, rSat); for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) { t_frequency::type frqType = static_cast(iFreq); _model._ionoCodeDelay[iFreq] = 40.3E16 / pow(t_CST::freq(frqType, _channel), 2) * stec; } } // Ocean Loading // ------------- // TODO // Set Model Set Flag // ------------------ _model._set = true; //printModel(); return success; } // //////////////////////////////////////////////////////////////////////////// void t_pppSatObs::printModel() const { LOG.setf(ios::fixed); LOG << "MODEL for Satellite " << _prn.toString() << endl << "RHO: " << setw(12) << setprecision(3) << _model._rho << endl << "ELE: " << setw(12) << setprecision(3) << _model._eleSat * 180.0 / M_PI << endl << "AZI: " << setw(12) << setprecision(3) << _model._azSat * 180.0 / M_PI << endl << "SATCLK: " << setw(12) << setprecision(3) << _model._satClkM << endl << "RECCLK: " << setw(12) << setprecision(3) << _model._recClkM << endl << "SAGNAC: " << setw(12) << setprecision(3) << _model._sagnac << endl << "ANTECC: " << setw(12) << setprecision(3) << _model._antEcc << endl << "TROPO: " << setw(12) << setprecision(3) << _model._tropo << endl << "WINDUP: " << setw(12) << setprecision(3) << _model._windUp << endl << "TIDES: " << setw(12) << setprecision(3) << _model._tide << endl; for (unsigned iFreq = 1; iFreq < t_frequency::max; iFreq++) { if (_obs[iFreq]) { string frqStr = t_frequency::toString(t_frequency::type(iFreq)); if (_prn.system() == frqStr[0]) { LOG << "PCO : " << frqStr << setw(12) << setprecision(3) << _model._antPCO[iFreq] << endl << "BIAS CODE : " << frqStr << setw(12) << setprecision(3) << _model._codeBias[iFreq] << endl << "BIAS PHASE : " << frqStr << setw(12) << setprecision(3) << _model._phaseBias[iFreq] << endl << "IONO CODEDELAY: " << frqStr << setw(12) << setprecision(3) << _model._ionoCodeDelay[iFreq] << endl; } } } for (unsigned ii = 0; ii < OPT->LCs(_prn.system()).size(); ii++) { t_lc::type tLC = OPT->LCs(_prn.system())[ii]; LOG << "OBS-CMP " << t_lc::toString(tLC) << ": " << _prn.toString() << " " << setw(12) << setprecision(3) << obsValue(tLC) << " " << setw(12) << setprecision(3) << cmpValue(tLC) << " " << setw(12) << setprecision(3) << obsValue(tLC) - cmpValue(tLC) << endl; } LOG << "OBS-CMP MW: " << _prn.toString() << " " << setw(12) << setprecision(3) << obsValue(t_lc::MW) << " " << setw(12) << setprecision(3) << cmpValue(t_lc::MW) << " " << setw(12) << setprecision(3) << obsValue(t_lc::MW) - cmpValue(t_lc::MW) << endl; } // //////////////////////////////////////////////////////////////////////////// double t_pppSatObs::cmpValueForBanc(t_lc::type tLC) const { return cmpValue(tLC) - _model._rho - _model._sagnac - _model._recClkM; } // //////////////////////////////////////////////////////////////////////////// double t_pppSatObs::cmpValue(t_lc::type tLC) const { if (!isValid(tLC)) { return 0.0; } // Non-Dispersive Part // ------------------- double nonDisp = _model._rho + _model._recClkM - _model._satClkM + _model._sagnac + _model._antEcc + _model._tropo + _model._tide; // Add Dispersive Part // ------------------- map codeCoeff; map phaseCoeff; lcCoeff(tLC, codeCoeff, phaseCoeff); double dispPart = 0.0; map::const_iterator it; for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) { t_frequency::type tFreq = it->first; dispPart += it->second * (_model._antPCO[tFreq] - _model._codeBias[tFreq] + _model._ionoCodeDelay[tFreq]); } for (it = phaseCoeff.begin(); it != phaseCoeff.end(); it++) { t_frequency::type tFreq = it->first; dispPart += it->second * (_model._antPCO[tFreq] - _model._phaseBias[tFreq] + _model._windUp * t_CST::lambda(tFreq, _channel) - _model._ionoCodeDelay[tFreq]); } return nonDisp + dispPart; } // //////////////////////////////////////////////////////////////////////////// void t_pppSatObs::setRes(t_lc::type tLC, double res) { _res[tLC] = res; } // //////////////////////////////////////////////////////////////////////////// double t_pppSatObs::getRes(t_lc::type tLC) const { map::const_iterator it = _res.find(tLC); if (it != _res.end()) { return it->second; } else { return 0.0; } }