Changeset 11079 in ntrip


Ignore:
Timestamp:
Oct 8, 2026, 11:47:35 AM (65 minutes ago)
Author:
stuerze
Message:

combi can be based on CoM

Location:
trunk/BNC
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/BNC/src/bncantex.cpp

    r11066 r11079  
    778778                                 const ColumnVector& xSat,
    779779                                 const ColumnVector& vSat, ColumnVector& dx,
    780                                  e_attMode mode, double externalYaw) {
     780                                 e_attMode mode, double externalYaw,
     781                                 const QString& stateKey) {
     782
     783  // separate yaw model states per caller (stateKey), each called in time order
     784  QString yawKey = stateKey.isEmpty() ? prn : stateKey + ":" + prn;
    781785
    782786  t_frequency::type frqType = t_frequency::dummy;
    … …  
    836840
    837841      if (mode == ATT_COMPUTED && prn[0] == 'R' && vSat.size() == 3) {
    838         psiEff = glonassYawAngle(prn, Mjd, xSat, vSat, xSun);
     842        psiEff = glonassYawAngle(yawKey, Mjd, xSat, vSat, xSun);
    839843        useVelocityFrame = true;
    840844      }
    841845      else if (mode == ATT_COMPUTED && prn[0] == 'G' && vSat.size() == 3
    842846               && !map->blockType.isEmpty()) {
    843         psiEff = gpsYawAngle(prn, map->blockType, Mjd, xSat, vSat, xSun);
     847        psiEff = gpsYawAngle(yawKey, map->blockType, Mjd, xSat, vSat, xSun);
    844848        useVelocityFrame = true;
    845849      }
    846850      else if (mode == ATT_COMPUTED && prn[0] == 'E' && vSat.size() == 3
    847851               && !map->blockType.isEmpty()) {
    848         psiEff = galileoYawAngle(prn, map->blockType, Mjd, xSat, vSat, xSun);
     852        psiEff = galileoYawAngle(yawKey, map->blockType, Mjd, xSat, vSat, xSun);
    849853        useVelocityFrame = true;
    850854      }
    851855      else if (mode == ATT_COMPUTED && prn[0] == 'C' && vSat.size() == 3
    852856               && !map->blockType.isEmpty()) {
    853         psiEff = bdsYawAngle(prn, map->blockType, Mjd, xSat, vSat, xSun);
     857        psiEff = bdsYawAngle(yawKey, map->blockType, Mjd, xSat, vSat, xSun);
    854858        useVelocityFrame = true;
    855859      }
  • trunk/BNC/src/bncantex.h

    r11066 r11079  
    6666                           ColumnVector& dx,
    6767                           e_attMode mode = ATT_COMPUTED,
    68                            double externalYaw = 0.0);
     68                           double externalYaw = 0.0,
     69                           const QString& stateKey = QString());
    6970
    7071  // Drain and return diagnostic logs accumulated by the yaw models.
  • trunk/BNC/src/bnchelp.html

    r11076 r11079  
    63236323  </p>
    63246324  <p>
     6325    Whether the orbit corrections refer to the satellite's Antenna Phase Center (APC) or to its Center of Mass (CoM) is
     6326    derived from the name of the correction stream or file: names beginning with 'SSRA' refer to the APC, the combined
     6327    corrections 'INTERNAL' always refer to the APC as well; all others are taken as CoM. For APC corrections, only the
     6328    differences of the satellite antenna offsets to that of the reference frequency are applied.
     6329  </p>
     6330  <p>
    63256331    Default value for 'ANTEX file' is an empty option field, meaning that you do not want to correct observations for
    63266332    Antenna Phase Center offsets and variations.
    … …  
    80968102    - despite the fact, that the delivered code biases of an AC may contain contributions from other biases, also phase
    80978103    biases.
     8104  </p>
     8105  <p>
     8106    <b>APC and CoM streams:</b> The orbits of Broadcast Correction streams refer either to the satellite's Antenna Phase
     8107    Center (APC, stream names 'SSRA...', reference frequency of the IGS-SSR and RTCM-SSR conventions) or to its Center
     8108    of Mass (CoM, 'SSRC...'). If streams of both kinds are combined and an ANTEX file is specified ('Upload
     8109    Corrections' panel), the combination is done in the CoM frame: the orbits of the APC streams are converted to CoM
     8110    with the satellite antenna offsets of the ANTEX file and the attitude model selected for the AC ('Computed', 'SSR'
     8111    or 'Nominal'). The result is converted to the APC for upload streams referring to the APC and for the combined
     8112    corrections passed on to BNC's PPP ('INTERNAL'), which always refer to the APC. As most ACs provide
     8113    both kinds of streams, combining CoM streams is recommended: the combined SP3 and CoM results then do not depend on
     8114    BNC's attitude model and ANTEX file, only the uploaded APC streams do. Without an ANTEX file, or if all streams
     8115    are of one kind, the combination is done in the frame of the master AC; streams of the other kind are not
     8116    combined, which is reported in the logfile at most once per hour per AC and system.
    80988117  </p>
    80998118  <p>
  • trunk/BNC/src/combination/bnccomb.cpp

    r11067 r11079  
    296296  }
    297297
     298  // Reference point of the combination: with ACs of both kinds (APC streams
     299  // 'SSRA...', CoM streams 'SSRC...') and an ANTEX file, the combination is
     300  // done in the CoM frame and the orbits of the APC ACs are converted to
     301  // CoM; otherwise in the frame of the master AC (ACs of the other kind are
     302  // not used)
     303  // ------------------------------------------------------------------------
     304  bool hasAPC = false, hasCoM = false;
     305  for (const cmbAC* AC : _ACs) {
     306    if (AC->isAPC) hasAPC = true; else hasCoM = true;
     307  }
     308  _cmbCoM = (_antex && hasAPC && hasCoM);
     309  if (_cmbCoM) {
     310    for (QMap<char, bool>::iterator it = _masterIsAPC.begin(); it != _masterIsAPC.end(); ++it) {
     311      it.value() = false;
     312    }
     313    emit newMessage("bncComb: APC and CoM streams configured - combination in the CoM frame, "
     314                    "orbits of the APC streams converted to CoM", true);
     315  }
     316  else if (hasAPC && hasCoM) {
     317    emit newMessage("bncComb: APC and CoM streams configured, but no ANTEX file - only the streams "
     318                    "with the reference point of the master AC are combined", true);
     319  }
     320  if (!_antex && hasCoM) {
     321    emit newMessage("bncComb: CoM streams configured, but no ANTEX file - the combined orbits (INTERNAL) "
     322                    "may refer to the CoM, while the PPP assumes the APC", true);
     323  }
     324
    298325
    299326  // Bias SINEX File
    … …  
    610637      }
    611638    }
    612     if (acName.isEmpty() ||
    613         isAPC != _masterIsAPC[sys] ||
    614         excludeSat(newClk._prn, excludeSats)) {
     639    if (acName.isEmpty() || excludeSat(newClk._prn, excludeSats)) {
     640      continue;
     641    }
     642    if (!_cmbCoM && isAPC != _masterIsAPC[sys]) {
     643      bncTime& warned = _refPointWarned[acName + sys];
     644      if (!warned.valid() || newClk._time - warned >= 3600.0) {
     645        warned = newClk._time;
     646        emit newMessage("bncComb: " + acName.toLatin1() + " " + QByteArray(1, sys) + " not combined: its orbits refer to the " +
     647                        (isAPC ? "APC" : "CoM") + ", those of the master AC " + _masterOrbitAC[sys].toLatin1() +
     648                        " to the " + (isAPC ? "CoM" : "APC") +
     649                        " (an ANTEX file allows to combine both in the CoM frame)", true);
     650      }
    615651      continue;
    616652    }
    … …  
    949985    }
    950986
     987    // Orbits of an APC AC in a combination in the CoM frame
     988    // ------------------------------------------------------
     989    if (_cmbCoM) {
     990      const cmbAC* AC = 0;
     991      for (const cmbAC* ac : _ACs) {
     992        if (ac->name == acName) {
     993          AC = ac;
     994          break;
     995        }
     996      }
     997      if (AC && AC->isAPC && convertApcToCoM(_newCorr, AC) != success) {
     998        delete _newCorr; _newCorr = 0;
     999        continue;
     1000      }
     1001    }
     1002
    9511003    // Store correction into the buffer
    9521004    // --------------------------------
    … …  
    10471099              << AC->name.toLatin1().data()   << "\n";
    10481100          _masterOrbitAC[sys] = AC->name;
    1049           _masterIsAPC[sys]   = AC->isAPC;
     1101          _masterIsAPC[sys]   = _cmbCoM ? false : AC->isAPC;
    10501102          break;
    10511103        }
    … …  
    13201372}
    13211373
     1374// Convert the orbit correction of an APC AC to the satellite's CoM: the
     1375// antenna offset (ANTEX, single reference frequency, attitude as configured
     1376// for the AC) is added to the correction in radial, along-track and
     1377// cross-track direction. The clock correction is not affected.
     1378////////////////////////////////////////////////////////////////////////////
     1379t_irc bncComb::convertApcToCoM(cmbCorr* corr, const cmbAC* AC) {
     1380  corr->_eph->setOrbCorr(&corr->_orbCorr);
     1381  corr->_eph->setClkCorr(&corr->_clkCorr);
     1382  ColumnVector xc(6), vv(3);
     1383  if (corr->_eph->getCrd(corr->_time, xc, vv, true) != success) {
     1384    return failure;
     1385  }
     1386  bncAntex::e_attMode attMode = bncAntex::ATT_COMPUTED;
     1387  double              extYaw  = 0.0;
     1388  if (AC->attitudeSource == "SSR" && corr->_satYawAngleValid) {
     1389    attMode = bncAntex::ATT_EXTERNAL;
     1390    extYaw  = corr->_satYawAngle;
     1391  }
     1392  else if (AC->attitudeSource == "Nominal") {
     1393    attMode = bncAntex::ATT_NOMINAL;
     1394  }
     1395  double Mjd = corr->_time.mjd() + corr->_time.daysec() / 86400.0;
     1396  ColumnVector dx(3); dx = 0.0;
     1397  if (_antex->satCoMcorrection(corr->_prn, Mjd, xc.Rows(1,3), vv, dx, attMode, extYaw, AC->name) != success) {
     1398    if (_antex->warnNoSatEntry(corr->_prn, corr->_time)) {
     1399      emit newMessage("bncComb: no satellite antenna entry valid at " +
     1400                      QByteArray(corr->_time.datestr().c_str()) + " " + QByteArray(corr->_time.timestr().c_str()) +
     1401                      " for " + corr->_prn.mid(0,3).toLatin1() +
     1402                      " in ANTEX file - APC orbit of " + AC->name.toLatin1() + " not converted to CoM, not combined", true);
     1403    }
     1404    return failure;
     1405  }
     1406  // X = X_brdc - RSW(xr): X_CoM = X_APC - dx  =>  xr_CoM = xr_APC + RSW(dx)
     1407  ColumnVector dRAO(3);
     1408  XYZ_to_RSW(xc.Rows(1,3), vv, dx, dRAO);
     1409  corr->_orbCorr._xr += dRAO;
     1410  return success;
     1411}
     1412
    13221413// Report AC satellites rejected as outliers in most epochs
    13231414////////////////////////////////////////////////////////////////////////////
    … …  
    15441635      apc(2) = xc(2)+dx(2);
    15451636      apc(3) = xc(3)+dx(3);
     1637    }
     1638
     1639    // The combined corrections passed on to the PPP (INTERNAL) always refer
     1640    // to the APC: orbits combined in the CoM frame are converted
     1641    if (!masterIsAPC && _antex) {
     1642      ColumnVector dRAO(3);
     1643      XYZ_to_RSW(xc.Rows(1,3), vv, dx, dRAO);
     1644      orbCorrections.back()._xr -= dRAO;
    15461645    }
    15471646
  • trunk/BNC/src/combination/bnccomb.h

    r11075 r11079  
    280280  void checkBiasConsistency(bncTime epoTime, char sys, QTextStream& out);
    281281  void checkPersistentOutliers(bncTime epoTime, char sys, QTextStream& out);
     282  t_irc convertApcToCoM(cmbCorr* corr, const cmbAC* AC);
     283  bool                                       _cmbCoM;          // combination in the CoM frame, APC ACs converted
     284  QMap<QString, bncTime>                     _refPointWarned;  // last 'APC/CoM differs from master' warning per AC
    282285  struct t_outlierStat {
    283286    t_outlierStat() : numEpo(0) {}
  • trunk/BNC/src/pppMain.cpp

    r11057 r11079  
    213213    if (_realTime) {
    214214      opt->_corrMount.assign(settings.value("PPP/corrMount").toString().toStdString());
    215       opt->_isAPC = (opt->_corrMount.substr(0,4)=="SSRA");
     215      // the combined corrections (INTERNAL) always refer to the APC
     216      opt->_isAPC = (opt->_corrMount.substr(0,4)=="SSRA" || opt->_corrMount == "INTERNAL");
    216217      opt->_biasMount.assign(settings.value("PPP/biasMount").toString().toStdString());
    217218      opt->_ionoMount.assign(settings.value("PPP/ionoMount").toString().toStdString());
  • trunk/BNC/src/src.pri

    r10917 r11079  
    4747              -lnewmat -lqwtpolar -lqwt
    4848
    49 HEADERS = bnchelp.html bncgetthread.h    bncwindow.h   bnctabledlg.h  \
     49HEADERS = bnchelp.html bncgetthread.h ssrQc.h    bncwindow.h   bnctabledlg.h  \
    5050          bnccaster.h bncrinex.h bnccore.h bncutils.h   bnchlpdlg.h   \
    5151          bncconst.h bnchtml.h bnctableitem.h bnczerodecoder.h        \
    … …  
    8989win32:HEADERS += serial/win_qextserialport.h
    9090
    91 SOURCES = bncgetthread.cpp  bncwindow.cpp bnctabledlg.cpp             \
     91SOURCES = bncgetthread.cpp ssrQc.cpp bncwindow.cpp bnctabledlg.cpp             \
    9292          bnccaster.cpp bncrinex.cpp bnccore.cpp bncutils.cpp         \
    9393          bncconst.cpp bnchtml.cpp bnchlpdlg.cpp bnctableitem.cpp     \
  • trunk/BNC/src/ssrQc.cpp

    r11075 r11079  
    106106  _dirName   = dirName;
    107107  _reference = reference.isEmpty() ? QString("CONSENSUS") : reference;
    108   bncSettings settings;
    109   QStringList cmbStreams = settings.value("cmbStreams").toStringList();
    110   if (!cmbStreams.isEmpty()) {
    111     _cmbMaster = cmbStreams[0].split(' ', Qt::SkipEmptyParts).value(0);
    112   }
    113108  _interval = interval > 0.0 ? interval : 3600.0;
    114109  for (const QString& sta : streams) {
    … …  
    398393
    399394// Reference point of the orbits of a stream: APC (SSRA...), CoM (SSRC...);
    400 // the combined stream INTERNAL refers to that of its master AC (the first
    401 // AC of the combination)
     395// the combined stream INTERNAL refers to the APC
    402396////////////////////////////////////////////////////////////////////////////
    403397char t_ssrQc::refPoint(const QString& staID) const {
    404   QString sta = (staID == "INTERNAL") ? _cmbMaster : staID;
     398  if (staID == "INTERNAL") {
     399    return 'A';  // the combined corrections always refer to the APC
     400  }
     401  const QString& sta = staID;
    405402  if (sta.startsWith("SSRA")) return 'A';
    406403  if (sta.startsWith("SSRC")) return 'C';
  • trunk/BNC/src/ssrQc.h

    r11075 r11079  
    107107  char   refPoint(const QString& staID) const;
    108108  QString                                      _reference;   // "CONSENSUS" or stream name
    109   QString                                      _cmbMaster;   // mountpoint of the combination's first AC
    110109  bncTime                                      _nextCmpEpoch;
    111110  QMap<QString, QMap<QString, QMap<double, t_orbCorr> > > _orbHist; // stream, PRN (internal), time
Note: See TracChangeset for help on using the changeset viewer.