Changeset 10946 in ntrip


Ignore:
Timestamp:
Jun 24, 2026, 4:39:53 PM (12 hours ago)
Author:
stuerze
Message:

added GLONASS-M yaw-fixed attitude model for the APC/CoM offset applied when saving SP3 files, reducing along-track/cross-track errors near the orbit noon/midnight points

Location:
trunk/BNC
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/BNC/CHANGELOG.md

    r10945 r10946  
    44- ADDED: L1C/B (RINEX code: '1E') in QZSS Signal ID Mapping
    55- ADDED: Minimum Elevation parameter to BNC's RINEX Editing & QC feature [(#195)](https://software.rtcm-ntrip.org/ticket/195)
     6- ADDED: GLONASS-M yaw-fixed attitude model for the APC/CoM offset applied when saving SP3 files, reducing along-track/cross-track errors near the orbit noon/midnight points
    67- FIXED: BNC is now able to work with com ports with higher numbers, e.g. COM11 or COM17  [(#205)](https://software.rtcm-ntrip.org/ticket/205)
    78- FIXED: Handling of ionospheric constraints [(#220)](https://software.rtcm-ntrip.org/ticket/220)
     9- FIXED: SP3/Clock RINEX clock values produced by the Upload Corrections feature no longer depend on whether 'Center of Mass' is ticked, since that setting should only affect the uploaded SSR Broadcast Correction stream
    810
    911
  • trunk/BNC/src/bncantex.cpp

    r10619 r10946  
    4040
    4141#include <iostream>
     42#include <cmath>
    4243#include <newmatio.h>
    4344
     
    330331}
    331332
     333// GLONASS Yaw Angle (Sun-pointing law overridden near noon/midnight when
     334// the satellite cannot mechanically keep up, following the GLONASS-M
     335// "yaw-fixed" behaviour described in Dilssner, Springer, Flohrer, Dow
     336// (2011), "The GLONASS-M satellite yaw-attitude model", Advances in Space
     337// Research 47(1), 160-171.
     338//
     339// Unlike GPS/Galileo/BeiDou, which are commonly approximated by the
     340// nominal Sun-pointing yaw-steering law of Bar-Sever (1996) at all times,
     341// GLONASS-M has been found to stop tracking that law and hold a constant
     342// (frozen) yaw angle whenever the required yaw rate would exceed the
     343// satellite's slew capability - which happens close to the orbit
     344// noon/midnight points whenever the Sun's elevation above the orbital
     345// plane (the "beta" angle) is small. The maximum yaw rate used below
     346// (0.25 deg/s) and the general approach follow that paper; satellite
     347// telemetry was not available to validate the exact rate against this
     348// installation's GLONASS satellites, so it should be checked against
     349// independently-known attitude/orbit residuals if high accuracy matters.
     350////////////////////////////////////////////////////////////////////////////
     351double bncAntex::glonassYawAngle(const QString& prn, const ColumnVector& xSat,
     352                                  const ColumnVector& vSat,
     353                                  const ColumnVector& xSun) {
     354
     355  const double MAX_YAW_RATE = 0.25 * M_PI / 180.0; // [rad/s], approximate
     356
     357  // Inertial-consistent velocity (xSat, vSat are Earth-fixed; remove the
     358  // Earth-rotation contribution so that the orbit normal below is not
     359  // contaminated by it)
     360  // -------------------------------------------------------------------
     361  ColumnVector Omega(3); Omega(1) = 0.0; Omega(2) = 0.0; Omega(3) = t_CST::omega;
     362  ColumnVector vInert = vSat + crossproduct(Omega, xSat);
     363
     364  // Orbit normal and instantaneous orbital rate from r x v (exact, valid
     365  // for any Keplerian orbit, not just circular ones)
     366  // ---------------------------------------------------------------------
     367  ColumnVector h = crossproduct(xSat, vInert);
     368  double       hNorm = sqrt(DotProduct(h, h));
     369  ColumnVector orbNormal = h / hNorm;
     370  double       r = sqrt(DotProduct(xSat, xSat));
     371  double       nRate = hNorm / (r * r); // [rad/s]
     372
     373  // Beta angle (Sun elevation above the orbital plane)
     374  // ----------------------------------------------------
     375  double beta = asin(DotProduct(orbNormal, xSun));
     376
     377  // Orbit angle mu, measured from the orbit midnight point, increasing in
     378  // the direction of satellite motion
     379  // -----------------------------------------------------------------------
     380  ColumnVector sunProj = xSun - DotProduct(xSun, orbNormal) * orbNormal;
     381  sunProj /= sqrt(DotProduct(sunProj, sunProj));
     382  ColumnVector eX = -1.0 * sunProj;                 // midnight direction, mu = 0
     383  ColumnVector eY = crossproduct(orbNormal, eX);
     384  ColumnVector rHat = xSat / r;
     385  double mu = atan2(DotProduct(rHat, eY), DotProduct(rHat, eX));
     386
     387  // Nominal Sun-pointing yaw angle and its rate (Bar-Sever 1996)
     388  // ----------------------------------------------------------------
     389  double tanBeta = tan(beta);
     390  double sinMu   = sin(mu);
     391  double psiNom  = atan2(-tanBeta, sinMu);
     392  double denom   = tanBeta * tanBeta + sinMu * sinMu;
     393  double psiRate = (denom > 1e-12)
     394                  ? nRate * fabs(tanBeta * cos(mu)) / denom
     395                  : 1e9;
     396
     397  t_glonassYaw& st = _glonassYaw[prn];
     398  double psiEff;
     399  if (psiRate > MAX_YAW_RATE) {
     400    if (!st.valid) {
     401      st.yaw = psiNom; // no prior history - best available estimate
     402    }
     403    psiEff = st.yaw;    // hold the frozen yaw angle
     404  }
     405  else {
     406    st.yaw   = psiNom;
     407    st.valid = true;
     408    psiEff   = psiNom;
     409  }
     410
     411  return psiEff;
     412}
     413
    332414// Satellite Antenna Offset
    333415////////////////////////////////////////////////////////////////////////////
    334416t_irc bncAntex::satCoMcorrection(const QString& prn, double Mjd,
    335                                  const ColumnVector& xSat, ColumnVector& dx) {
     417                                 const ColumnVector& xSat,
     418                                 const ColumnVector& vSat, ColumnVector& dx) {
    336419
    337420  t_frequency::type frqType = t_frequency::dummy;
     
    374457      xSun /= sqrt(DotProduct(xSun,xSun));
    375458
    376       ColumnVector sy = crossproduct(sz, xSun);
    377       sy /= sqrt(DotProduct(sy,sy));
    378 
    379       ColumnVector sx = crossproduct(sy, sz);
     459      ColumnVector sy, sx;
     460
     461      // GLONASS: override the nominal Sun-pointing attitude near the
     462      // orbit noon/midnight points when the beta angle is small (see
     463      // glonassYawAngle() above). Elsewhere GLONASS-M follows the same
     464      // nominal law as the other constellations, so the result is
     465      // identical to the direct Sun-pointing computation used below.
     466      // -----------------------------------------------------------------
     467      if (prn[0] == 'R' && vSat.size() == 3) {
     468        double psi = glonassYawAngle(prn, xSat, vSat, xSun);
     469
     470        ColumnVector vInert = vSat;
     471        ColumnVector Omega(3); Omega(1) = 0.0; Omega(2) = 0.0; Omega(3) = t_CST::omega;
     472        vInert += crossproduct(Omega, xSat);
     473
     474        ColumnVector sy0 = crossproduct(sz, vInert);
     475        sy0 /= sqrt(DotProduct(sy0,sy0));
     476        ColumnVector sx0 = crossproduct(sy0, sz);
     477
     478        // Rodrigues rotation of (sx0, sy0) around sz by angle psi
     479        double cosY = cos(psi);
     480        double sinY = sin(psi);
     481        sx = sx0 * cosY + crossproduct(sz, sx0) * sinY;
     482        sy = sy0 * cosY + crossproduct(sz, sy0) * sinY;
     483      }
     484      else {
     485        sy = crossproduct(sz, xSun);
     486        sy /= sqrt(DotProduct(sy,sy));
     487        sx = crossproduct(sy, sz);
     488      }
    380489
    381490      dx[0] = sx[0] * neu[0] + sy[0] * neu[1] + sz[0] * neu[2];
  • trunk/BNC/src/bncantex.h

    r10130 r10946  
    4747                  double eleSat, double azSat, bool& found) const;
    4848  t_irc   satCoMcorrection(const QString& prn, double Mjd,
    49                            const ColumnVector& xSat, ColumnVector& dx);
     49                           const ColumnVector& xSat, const ColumnVector& vSat,
     50                           ColumnVector& dx);
    5051
    5152 private:
     53  // Per-satellite GLONASS yaw state, used to freeze the yaw angle while
     54  // the satellite cannot follow the nominal Sun-pointing attitude law
     55  // (see glonassYawAngle() below).
     56  class t_glonassYaw {
     57   public:
     58    t_glonassYaw() {
     59      yaw   = 0.0;
     60      valid = false;
     61    }
     62    double yaw;
     63    bool   valid;
     64  };
     65
     66  double glonassYawAngle(const QString& prn, const ColumnVector& xSat,
     67                          const ColumnVector& vSat, const ColumnVector& xSun);
     68
     69  QMap<QString, t_glonassYaw> _glonassYaw;
     70
    5271  class t_frqMap {
    5372   public:
  • trunk/BNC/src/bnchelp.html

    r10945 r10946  
    56405640</p>
    56415641<p>
     5642For GLONASS satellites, that APC/CoM offset is rotated into the satellite-fixed frame using the satellite's
     5643yaw attitude. BNC assumes the nominal Sun-pointing yaw-steering law used for the other GNSS systems, except
     5644close to the orbit noon and midnight points when the Sun's elevation above the orbital plane (the 'beta'
     5645angle) is small. There, GLONASS-M satellites are known to stop tracking that law and instead hold the yaw
     5646angle fixed, following the model described in Dilssner, Springer, Flohrer, Dow (2011), 'The GLONASS-M
     5647satellite yaw-attitude model', Advances in Space Research 47(1), 160-171. Without that correction, the
     5648converted GLONASS CoM position can be off by several decimeters in the along-track and cross-track
     5649components in that situation. The maximum yaw rate used to decide when GLONASS-M can no longer follow the
     5650nominal law (0.25 deg/s) is taken from the literature and not calibrated against any specific satellite, so
     5651results should be checked against independently known attitude or orbit information where high accuracy on
     5652GLONASS is required.
     5653</p>
     5654<p>
    56425655 Note that clocks in the SP3 orbit files are not corrected for the conventional periodic relativistic effect.
    56435656</p>
  • trunk/BNC/src/combination/bnccomb.cpp

    r10890 r10946  
    12051205      char sys = corr->_eph->prn().system();
    12061206      masterIsAPC = _masterIsAPC[sys];
    1207       if (_antex->satCoMcorrection(corr->_prn, Mjd, xc.Rows(1,3), dx) != success) {
     1207      if (_antex->satCoMcorrection(corr->_prn, Mjd, xc.Rows(1,3), vv, dx) != success) {
    12081208        dx = 0;
    12091209        _log += "antenna not found " + corr->_prn.mid(0,3).toLatin1() + '\n';
Note: See TracChangeset for help on using the changeset viewer.