Changeset 11110 in ntrip


Ignore:
Timestamp:
Oct 22, 2026, 11:10:34 AM (7 hours ago)
Author:
stuerze
Message:

clock combination: some updates regarding attitude correction

Location:
trunk/BNC
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/BNC/CHANGELOG.md

    r11109 r11110  
    1313- CHANGED: Combination: the random walk of the AC satellite offsets is defined per time (1 cm per 10 s) instead of per epoch, so the result no longer depends on the sampling interval (at 5 s the offsets were sqrt(2) noisier per hour)
    1414- CHANGED: Combination: an AC excluded from the clock datum because of inconsistent code biases is used again only after 12 passing checks in a row (1 hour), not after the first one (a borderline AC changed the clock datum every 5 minutes)
    15 - CHANGED: Combination: attitude consistency correction dC_att of the clocks reactivated: a yaw angle difference between an AC and the master orbit AC (SSR yaw angle or BNC's attitude model, as selected in 'Attitude') changes the ionosphere-free phase wind-up and thus the AC's clock (lambda_IF * dyaw / 2pi), zero for ACs using the same attitude model
     15- CHANGED: Combination: attitude consistency correction dC_att of the clocks reactivated: a yaw angle difference between an AC and the master orbit AC (SSR yaw angle or BNC's attitude model, as selected in 'Attitude') changes the ionosphere-free phase wind-up and thus the AC's clock (lambda_IF * dyaw / 2pi, yaw difference followed over time, so a turn in the opposite direction leaves one cycle), zero for ACs using the same attitude model
    1616- ADDED: Combination of APC (SSRA) and CoM (SSRC) streams together: with an ANTEX file the combination is done in the CoM frame, the orbits of the APC streams are converted to CoM; ACs not combined because of a different reference point are reported
    1717- ADDED: Option 'Reference Signals' (key ssrRefSignals): reference signals of the satellite clocks per GNSS for the combination (AC clock referral, code biases from the Bias SINEX file) and the SSR QC, default IGS Real-Time convention G:1W&2W R:1P&2P E:1C&5Q C:2I&6I J:1C&2L S:1C&5Q, e.g. for a switch of BDS to C1X/C5X or of QZSS to L1/L5
    … …  
    3737- FIXED: Combination: the satellite offset of an AC was not reset after an outlier
    3838- FIXED: Combination: text of the combination logfile was lost
     39- FIXED: GPS attitude model ('Computed'): at the end of a noon/midnight turn the yaw jumped to the nominal yaw (e.g. by 95 deg); the satellite now keeps yawing at its maximum rate until it has caught up with the nominal yaw
     40- FIXED: GLONASS attitude model ('Computed'): the yaw was frozen while the nominal yaw rate exceeded the maximum rate and then jumped to the nominal yaw; now following Dilssner et al. (2011), the satellite yaws at the maximum rate (0.25 deg/s) during noon turns until it has caught up, and in the Earth's shadow until it reaches the nominal yaw at shadow exit
    3941- FIXED: Upload of raw streams lost data chunks
    4042- FIXED: RTCM3 decoder: a 0xD3 byte followed by a zero length field at the end of the received data caused an endless loop until std::bad_alloc, and the stream was deleted
  • trunk/BNC/src/bncantex.cpp

    r11104 r11110  
    385385}
    386386
    387 // GLONASS Yaw Angle (Sun-pointing law overridden near noon/midnight when
    388 // the satellite cannot mechanically keep up, following the GLONASS-M
    389 // "yaw-fixed" behaviour described in Dilssner, Springer, Flohrer, Dow
    390 // (2011), "The GLONASS-M satellite yaw-attitude model", Advances in Space
    391 // Research 47(1), 160-171.
    392 //
    393 // Unlike GPS/Galileo/BeiDou, which are commonly approximated by the
    394 // nominal Sun-pointing yaw-steering law of Bar-Sever (1996) at all times,
    395 // GLONASS-M has been found to stop tracking that law and hold a constant
    396 // (frozen) yaw angle whenever the required yaw rate would exceed the
    397 // satellite's slew capability - which happens close to the orbit
    398 // noon/midnight points whenever the Sun's elevation above the orbital
    399 // plane (the "beta" angle) is small. The maximum yaw rate used below
    400 // (0.25 deg/s) and the general approach follow that paper; satellite
    401 // telemetry was not available to validate the exact rate against this
    402 // installation's GLONASS satellites, so it should be checked against
    403 // independently-known attitude/orbit residuals if high accuracy matters.
     387// GLONASS Yaw Angle following the GLONASS-M yaw-attitude model of Dilssner,
     388// Springer, Gienger, Dow (2011), "The GLONASS-M satellite yaw-attitude
     389// model", Advances in Space Research 47(1), 160-171:
     390//  - noon turn: when the nominal yaw rate exceeds the maximum hardware rate
     391//    (0.25 deg/s), the satellite yaws at that rate in the direction of the
     392//    nominal yaw rate until it has caught up with the nominal yaw;
     393//  - midnight turn: in the Earth's shadow the satellite yaws at the maximum
     394//    rate until it reaches the nominal yaw at shadow exit and holds it until
     395//    leaving the shadow.
     396// The maximum rate is applied to all GLONASS satellites (K/K2 like M).
    404397////////////////////////////////////////////////////////////////////////////
    405398double bncAntex::glonassYawAngle(const QString& prn, double Mjd,
    … …  
    411404
    412405  // This bounds the gap BETWEEN CONSECUTIVE CALLS for a given satellite
    413   // (not how long the freeze itself has lasted - a real low-beta passage
    414   // can legitimately stay frozen far longer than this). If there was no
     406  // (not how long a turn lasts). If there was no
    415407  // call for longer than this, something else interrupted normal epoch-by-
    416408  // epoch processing (a stream outage, a maneuver-flagged-unhealthy
    417   // period, ...), so the stored frozen value is more likely stale than a
    418   // still-valid freeze, and we fall back to the current nominal value
     409  // period, ...), so the stored yaw is more likely stale than a
     410  // still-valid turn, and we fall back to the current nominal value
    419411  // instead of trusting it indefinitely.
    420412  const double MAX_CALL_GAP = 1800.0 / 86400.0; // 30 minutes, in days
    … …  
    460452                  : 1e9;
    461453
     454  // Sign of the nominal yaw rate: dPsi/dt = nRate * tanBeta * cos(mu) / denom
     455  double psiDotNomSign = (tanBeta * cos(mu) >= 0.0) ? 1.0 : -1.0;
     456
     457  // Earth's shadow (cylinder): around midnight for |mu| < muShadow
     458  // ----------------------------------------------------------------
     459  const double RE = 6378137.0;
     460  double cosMuShadow = sqrt(std::max(0.0, 1.0 - RE * RE / (r * r))) / cos(beta);
     461  bool   inShadow    = (cosMuShadow < 1.0 && cos(mu) > cosMuShadow);
     462  double muExit      = (cosMuShadow < 1.0) ? acos(cosMuShadow) : 0.0;
     463  double psiExit     = atan2(-tanBeta, sin(muExit)); // nominal yaw at shadow exit
     464
    462465  t_glonassYaw& st = _glonassYaw[prn];
    463466  double callGap = st.valid ? (Mjd - st.lastCallMjd) : 0.0;
     467  if (st.valid && callGap > MAX_CALL_GAP) {
     468    _glonassYawLog += QString().asprintf(
     469      "%s glonassYaw STALE-RESET  Mjd=%.6f beta=%6.3f mu=%7.2f rate=%6.3f"
     470      " old=%7.2f new=%7.2f gap=%.1fmin\n",
     471      prn.toLatin1().data(), Mjd, beta*180.0/M_PI, mu*180.0/M_PI,
     472      psiRate*180.0/M_PI, st.yaw*180.0/M_PI, psiNom*180.0/M_PI,
     473      callGap*1440.0);
     474    st.valid = false;
     475    st.fixed = false;
     476  }
     477  double step = MAX_YAW_RATE * callGap * 86400.0;  // max. yaw change since the last call
    464478  double psiEff;
    465   if (psiRate > MAX_YAW_RATE) {
    466     if (!st.valid || callGap > MAX_CALL_GAP) {
    467       if (st.valid) { // i.e. it was a genuine gap, not cold-start
    468         _glonassYawLog += QString().asprintf(
    469           "%s glonassYaw STALE-RESET  Mjd=%.6f beta=%6.3f mu=%7.2f rate=%6.3f"
    470           " old=%7.2f new=%7.2f gap=%.1fmin\n",
    471           prn.toLatin1().data(), Mjd, beta*180.0/M_PI, mu*180.0/M_PI,
    472           psiRate*180.0/M_PI, st.yaw*180.0/M_PI, psiNom*180.0/M_PI,
    473           callGap*1440.0);
    474       }
    475       st.yaw = psiNom; // no prior history, or the gap is too long to trust it
    476     }
     479
     480  if (inShadow) {
     481    // Midnight turn: in the shadow the satellite yaws at the maximum rate
     482    // until it reaches the nominal yaw at shadow exit and holds it there
    477483    if (!st.fixed) {
     484      st.yaw      = st.valid ? st.yaw : psiNom;
     485      st.turnSign = psiDotNomSign;
     486      st.fixed    = true;
    478487      _glonassYawLog += QString().asprintf(
    479         "%s glonassYaw FIXED-ENTER  Mjd=%.6f beta=%6.3f mu=%7.2f rate=%6.3f yaw=%7.2f\n",
     488        "%s glonassYaw SHADOW-ENTER Mjd=%.6f beta=%6.3f mu=%7.2f yaw=%7.2f exit=%7.2f\n",
     489        prn.toLatin1().data(), Mjd, beta*180.0/M_PI, mu*180.0/M_PI,
     490        st.yaw*180.0/M_PI, psiExit*180.0/M_PI);
     491      psiEff = st.yaw;
     492    }
     493    else if (remainder(st.turnSign * (psiExit - st.yaw), 2.0 * M_PI) > step) {
     494      psiEff = st.yaw + st.turnSign * step;
     495    }
     496    else {
     497      psiEff = psiExit;
     498    }
     499  }
     500  else if (psiRate > MAX_YAW_RATE) {
     501    // Noon turn (or shadow-free midnight turn): the satellite yaws at the
     502    // maximum rate in the direction of the nominal yaw rate
     503    if (!st.valid || !st.fixed) {
     504      st.yaw      = st.valid ? st.yaw : psiNom;
     505      st.turnSign = psiDotNomSign;
     506      st.fixed    = true;
     507      _glonassYawLog += QString().asprintf(
     508        "%s glonassYaw TURN-ENTER   Mjd=%.6f beta=%6.3f mu=%7.2f rate=%6.3f yaw=%7.2f\n",
    480509        prn.toLatin1().data(), Mjd, beta*180.0/M_PI, mu*180.0/M_PI,
    481510        psiRate*180.0/M_PI, st.yaw*180.0/M_PI);
    482       st.fixed = true;
    483     }
    484     psiEff = st.yaw;    // hold the frozen yaw angle
     511      psiEff = st.yaw;
     512    }
     513    else {
     514      psiEff = st.yaw + st.turnSign * step;
     515    }
     516  }
     517  else if (st.valid && st.fixed &&
     518           remainder(st.turnSign * (psiNom - st.yaw), 2.0 * M_PI) > step) {
     519    // after the turn or shadow exit, the satellite still lags behind and
     520    // keeps yawing at the maximum rate until it has caught up
     521    psiEff = st.yaw + st.turnSign * step;
    485522  }
    486523  else {
     524    // Nominal tracking
    487525    if (st.fixed) {
    488526      _glonassYawLog += QString().asprintf(
    489         "%s glonassYaw FIXED-EXIT   Mjd=%.6f beta=%6.3f mu=%7.2f rate=%6.3f"
    490         " frozen=%7.2f nominal=%7.2f\n",
     527        "%s glonassYaw TURN-EXIT    Mjd=%.6f beta=%6.3f mu=%7.2f rate=%6.3f"
     528        " yaw=%7.2f nominal=%7.2f\n",
    491529        prn.toLatin1().data(), Mjd, beta*180.0/M_PI, mu*180.0/M_PI,
    492530        psiRate*180.0/M_PI, st.yaw*180.0/M_PI, psiNom*180.0/M_PI);
    493531      st.fixed = false;
    494532    }
    495     st.yaw = psiNom;
    496533    psiEff = psiNom;
    497534  }
    498   st.lastCallMjd = Mjd; // update on every call, fixed or not, to detect gaps
     535  while (psiEff >  M_PI) psiEff -= 2.0 * M_PI;
     536  while (psiEff < -M_PI) psiEff += 2.0 * M_PI;
     537
     538  st.yaw         = psiEff;
     539  st.lastCallMjd = Mjd; // update on every call to detect gaps
    499540  st.valid       = true;
    500541
    … …  
    607648        psiNom*180.0/M_PI, psiEff*180.0/M_PI,
    608649        psiDotNomAbs*psiDotNomSign*180.0/M_PI, psiDotMax*180.0/M_PI);
    609       st.inTurn = true;
     650      st.inTurn   = true;
     651      st.turnSign = psiDotNomSign;
    610652    } else {
    611653      // Continuing the turn: integrate at constrained rate
    612654      double dt  = callGap * 86400.0; // [s]
    613       psiEff = st.yaw + psiDotNomSign * psiDotMax * dt;
     655      psiEff = st.yaw + st.turnSign * psiDotMax * dt;
    614656    }
    615657    // Wrap to [-pi, pi]
     658    while (psiEff >  M_PI) psiEff -= 2.0 * M_PI;
     659    while (psiEff < -M_PI) psiEff += 2.0 * M_PI;
     660  }
     661  else if (st.valid && st.inTurn &&
     662           remainder(st.turnSign * (psiNom - st.yaw), 2.0 * M_PI) > psiDotMax * callGap * 86400.0) {
     663    // End of the turn: the nominal yaw rate is below the maximum again, but
     664    // the satellite still lags behind and keeps yawing at the maximum rate
     665    // until it has caught up with the nominal yaw (Kouba 2009)
     666    psiEff = st.yaw + st.turnSign * psiDotMax * callGap * 86400.0;
    616667    while (psiEff >  M_PI) psiEff -= 2.0 * M_PI;
    617668    while (psiEff < -M_PI) psiEff += 2.0 * M_PI;
    … …  
    628679      st.inTurn = false;
    629680    }
    630     st.yaw = psiNom;
    631681  }
    632682
    … …  
    837887      // ATT_NOMINAL: simple nominal Sun-pointing for all systems.
    838888      // ATT_COMPUTED or ATT_EXTERNAL: use per-system attitude models.
    839       //   GLONASS  → yaw-fixed model (Dilssner et al. 2011)
     889      //   GLONASS  → noon/shadow turn model (Dilssner et al. 2011)
    840890      //   GPS      → noon/midnight turn model (Kouba 2009/2015)
    841891      //   others   → simple nominal Sun-pointing
  • trunk/BNC/src/bncantex.h

    r11104 r11110  
    5757  // Attitude model selection for satCoMcorrection().
    5858  enum e_attMode {
    59     ATT_COMPUTED = 0,  // full model: GLONASS yaw-fixed + GPS noon/midnight turn
     59    ATT_COMPUTED = 0,  // full model: GLONASS noon/shadow turns, GPS noon/midnight turns, Galileo/BDS orbit-normal mode
    6060    ATT_NOMINAL  = 1,  // simple nominal Sun-pointing only (no maneuver model)
    6161    ATT_EXTERNAL = 2,  // use caller-supplied externalYaw angle [rad]
    … …  
    8989
    9090 private:
    91   // Per-satellite GLONASS yaw state: freezes the yaw angle while the
    92   // satellite cannot follow the nominal Sun-pointing law (Dilssner 2011).
     91  // Per-satellite GLONASS yaw state: rate-limited yaw during noon turns and
     92  // in the Earth's shadow (Dilssner 2011).
    9393  class t_glonassYaw {
    9494   public:
    … …  
    9898      valid       = false;
    9999      fixed       = false;
     100      turnSign    = 1.0;
    100101    }
    101102    double yaw;
    102103    double lastCallMjd; // Mjd of the most recent call (any branch) for this PRN
    103104    bool   valid;
    104     bool   fixed;   // true while the yaw-fixed override is currently active
     105    bool   fixed;     // true during a noon/midnight turn or the catch-up after it
     106    double turnSign;  // direction of the turn (sign of the nominal yaw rate at its start)
    105107  };
    106108
    … …  
    115117      valid       = false;
    116118      inTurn      = false;
     119      turnSign    = 1.0;
    117120    }
    118121    double yaw;
    119122    double lastCallMjd;
    120123    bool   valid;
    121     bool   inTurn;  // true while in a constrained noon/midnight turn
     124    bool   inTurn;    // true while in a constrained noon/midnight turn
     125    double turnSign;  // direction of the turn (sign of the nominal yaw rate at its start)
    122126  };
    123127
  • trunk/BNC/src/bnchelp.html

    r11108 r11110  
    79867986</pre>
    79877987  <pre>
    7988  dC_att = (yawAngle_AC - yawAngle_MasterAC) / (2*PI) * wavelength(IF)
     7988 dC_att = (yawAngle_MasterAC - yawAngle_AC) / (2*PI) * wavelength(IF)
    79897989</pre>
    79907990  <p>
    … …  
    79927992    ionosphere-free combination of the reference signals (about 10.7 cm for GPS). The yaw angle of each AC is taken
    79937993    from its attitude selected in the 'Attitude' field (SSR yaw angle, or BNC's 'Computed' or 'Nominal' model); the
    7994     yaw angle difference is reduced to +-180 degrees. dC_att is therefore zero for two ACs with the same attitude model
     7994    yaw angle difference is followed continuously over time, so a turn of the AC in the opposite direction than
     7995    the master orbit AC leaves one full cycle (wavelength(IF)) in its clock, as observed in the clocks. dC_att is therefore zero for two ACs with the same attitude model
    79957996    and differs from zero mainly during eclipse seasons and noon/midnight turns, if an AC sends its yaw angles and
    79967997    'SSR' is selected for it. A constant part is absorbed by the AC's satellite offset. dC_att requires an ANTEX file.
    … …  
    83208321    <li><b>Computed</b> (default): BNC applies its own kinematic attitude model:
    83218322      GPS noon/midnight turn manoeuvres (Kouba 2009/2015, Bar-Sever 1996),
    8322       GLONASS yaw-fixed mode (Dilssner et al. 2011), and
     8323      GLONASS-M noon turns and midnight turns in the Earth's shadow (Dilssner et al. 2011), and
    83238324      Galileo / BDS orbit-normal mode switching (Kouba 2017, Dai et al. 2015, Steigenberger et al. 2018).</li>
    83248325    <li><b>Nominal</b>: a simplified, continuous Sun-pointing model is used without any manoeuvre modelling.</li>
    … …  
    92879288    yaw attitude. BNC assumes the nominal Sun-pointing yaw-steering law used for the other GNSS systems, except
    92889289    close to the orbit noon and midnight points when the Sun's elevation above the orbital plane (the 'beta'
    9289     angle) is small. There, GLONASS-M satellites are known to stop tracking that law and instead hold the yaw
    9290     angle fixed, following the model described in Dilssner, Springer, Flohrer, Dow (2011), 'The GLONASS-M
     9290    angle) is small. There, GLONASS-M satellites cannot follow that law: at noon they yaw at their maximum rate
     9291    in the direction of the nominal yaw rate until they have caught up with the nominal yaw; in the Earth's shadow
     9292    they yaw at the maximum rate until they reach the nominal yaw at shadow exit and hold it until leaving the
     9293    shadow. This follows the model described in Dilssner, Springer, Gienger, Dow (2011), 'The GLONASS-M
    92919294    satellite yaw-attitude model', Advances in Space Research 47(1), 160-171. Without that correction, the
    92929295    converted GLONASS CoM position can be off by several decimeters in the along-track and cross-track
  • trunk/BNC/src/bncwindow.cpp

    r11087 r11110  
    17401740  // WhatsThis, Combine Corrections
    17411741  // ------------------------------
    1742   _cmbTable->setWhatsThis(tr("<p>BNC allows to process several orbit and clock correction streams in real-time to produce, encode, upload and save a combination of correctors coming from different providers. </p><p>To add a line to the 'Combine Corrections' table hit the 'Add Row' button, double click on the 'Mountpoint' field to specify a Broadcast Ephemeris Correction mountpoint from the 'Streams' section below and hit Enter. Then double click on the 'AC Name' field to enter your choice of an abbreviation for the Analysis Center (AC) providing the stream. Double click on the 'Weight Factor' field to enter a weight factor to be applied for this stream in the combination. A Factor greater than 1 will enlarge the sigma of the clock pseudo-observations and with it down-weight its contribution. Finally, double click on the 'Exclude Satellites' field and specify satellites, to exclude them for an individual AC. An entry 'G04,G31,R' means to excludes GPS satellites PRN 4 and 31 as well as all GLONASS satellites from one individual AC. Default is an empty option field, meaning that no satellite is excluded from this individual AC.</p><p>Use the 'Attitude' field to select how satellite attitude is modelled when converting Antenna Phase Center (APC) corrections to Center-of-Mass (CoM) positions for SP3 output. 'Computed' (default) applies BNC's kinematic attitude model, including GPS noon/midnight turn manoeuvres, GLONASS yaw-fixed mode, and Galileo/BDS orbit-normal mode switching. 'Nominal' uses a simplified continuous Sun-pointing model without manoeuvre modelling. 'SSR' uses the yaw angle transmitted in the SSR phase bias message; select this option only if you trust the yaw values provided by the AC. If no yaw angle is available for a particular satellite in a given epoch, BNC falls back to 'Computed'. Note that the attitude model affects APC-referenced streams only; for CoM-referenced streams the AC has already applied attitude before encoding.</p><p>Note that the orbit information in the resulting combination stream is just copied from one of the incoming streams. The stream used for providing the orbits may vary over time: if the orbit providing stream has an outage then BNC switches to the next remaining stream for getting hold of the orbit information.</p><p>The combination process requires Broadcast Ephemeris. Besides orbit and clock correction streams BNC should therefore pull a stream carrying Broadcast Ephemeris in the form of RTCM Version 3 messages.</p><p>It is possible to specify only one Broadcast Ephemeris Correction stream in the 'Combine Corrections' table. Instead of combining corrections BNC will then add the corrections to the Broadcast Ephemeris with the possibility to save final orbit and clock results in SP3 and/or Clock RINEX format. <i>[key: cmbStreams]</i></p>"));
     1742  _cmbTable->setWhatsThis(tr("<p>BNC allows to process several orbit and clock correction streams in real-time to produce, encode, upload and save a combination of correctors coming from different providers. </p><p>To add a line to the 'Combine Corrections' table hit the 'Add Row' button, double click on the 'Mountpoint' field to specify a Broadcast Ephemeris Correction mountpoint from the 'Streams' section below and hit Enter. Then double click on the 'AC Name' field to enter your choice of an abbreviation for the Analysis Center (AC) providing the stream. Double click on the 'Weight Factor' field to enter a weight factor to be applied for this stream in the combination. A Factor greater than 1 will enlarge the sigma of the clock pseudo-observations and with it down-weight its contribution. Finally, double click on the 'Exclude Satellites' field and specify satellites, to exclude them for an individual AC. An entry 'G04,G31,R' means to excludes GPS satellites PRN 4 and 31 as well as all GLONASS satellites from one individual AC. Default is an empty option field, meaning that no satellite is excluded from this individual AC.</p><p>Use the 'Attitude' field to select how satellite attitude is modelled when converting Antenna Phase Center (APC) corrections to Center-of-Mass (CoM) positions for SP3 output. 'Computed' (default) applies BNC's kinematic attitude model, including GPS noon/midnight turn manoeuvres, GLONASS-M noon/shadow turns, and Galileo/BDS orbit-normal mode switching. 'Nominal' uses a simplified continuous Sun-pointing model without manoeuvre modelling. 'SSR' uses the yaw angle transmitted in the SSR phase bias message; select this option only if you trust the yaw values provided by the AC. If no yaw angle is available for a particular satellite in a given epoch, BNC falls back to 'Computed'. Note that the attitude model affects APC-referenced streams only; for CoM-referenced streams the AC has already applied attitude before encoding.</p><p>Note that the orbit information in the resulting combination stream is just copied from one of the incoming streams. The stream used for providing the orbits may vary over time: if the orbit providing stream has an outage then BNC switches to the next remaining stream for getting hold of the orbit information.</p><p>The combination process requires Broadcast Ephemeris. Besides orbit and clock correction streams BNC should therefore pull a stream carrying Broadcast Ephemeris in the form of RTCM Version 3 messages.</p><p>It is possible to specify only one Broadcast Ephemeris Correction stream in the 'Combine Corrections' table. Instead of combining corrections BNC will then add the corrections to the Broadcast Ephemeris with the possibility to save final orbit and clock results in SP3 and/or Clock RINEX format. <i>[key: cmbStreams]</i></p>"));
    17431743  addCmbRowButton->setWhatsThis(tr("<p>Hit 'Add Row' button to add another line to the 'Combine Corrections' table.</p>"));
    17441744  delCmbRowButton->setWhatsThis(tr("<p>Hit 'Delete' button to delete the highlighted line(s) from the 'Combine Corrections' table.</p>"));
  • trunk/BNC/src/combination/bnccomb.cpp

    r11107 r11110  
    16981698        continue;
    16991699      }
    1700       QString glonassYawLog = _antex->takeGlonassYawLog();
    1701       if (!glonassYawLog.isEmpty()) {
    1702         emit newMessage(("bncComb: " + glonassYawLog.trimmed()).toLatin1(), false);
    1703       }
    1704       QString gpsYawLog = _antex->takeGpsYawLog();
    1705       if (!gpsYawLog.isEmpty()) {
    1706         emit newMessage(("bncComb: " + gpsYawLog.trimmed()).toLatin1(), false);
    1707       }
    1708       QString onYawLog = _antex->takeOnYawLog();
    1709       if (!onYawLog.isEmpty()) {
    1710         emit newMessage(("bncComb: " + onYawLog.trimmed()).toLatin1(), false);
     1700      // transitions of the output attitude only, not of the AC attitudes
     1701      // used for the clock consistency (state keys "<AC>:att")
     1702      QStringList yawLogs = (_antex->takeGlonassYawLog() + _antex->takeGpsYawLog() +
     1703                             _antex->takeOnYawLog()).split('\n', Qt::SkipEmptyParts);
     1704      for (const QString& line : yawLogs) {
     1705        if (!line.contains(":att:")) {
     1706          emit newMessage(("bncComb: " + line.trimmed()).toLatin1(), false);
     1707        }
    17111708      }
    17121709    }
    … …  
    18851882    double dC_att = 0.0;
    18861883    if (corr->_attYawValid && masterCorr[prn]->_attYawValid) {
    1887       double dPsi = remainder(corr->_attYaw - masterCorr[prn]->_attYaw, 2.0 * M_PI);
     1884      // unwrapped over time: a turn in opposite directions accumulates a
     1885      // full cycle of wind-up, which stays in the clock (reset after 30 min);
     1886      // sign confirmed with the clocks of an AC turning opposite to the master;
     1887      // a yaw rotation +dpsi (right-handed about the antenna boresight) shifts
     1888      // the carrier phase by -dpsi/2pi cycles; an AC modelling its yaw larger by
     1889      // dpsi gets its clock (IGS sense) smaller by lambda_IF * dpsi / 2pi:
     1890      // dClk_AC - dClk_master = lambda_IF * (psi_master - psi_AC) / 2pi.
     1891      // Chen et al. (2022, EPS 74:142, Eq. 4) give the opposite
     1892      // sign, but did not apply the correction (no attitude products).
     1893      double  dPsi = remainder(masterCorr[prn]->_attYaw - corr->_attYaw, 2.0 * M_PI);
     1894      QString key  = corr->_acName + ' ' + masterCorr[prn]->_acName + ' ' + prn;
     1895      if (_dPsiLast.contains(key) && corr->_time - _dPsiLast[key].second <= 1800.0) {
     1896        double last = _dPsiLast[key].first;
     1897        dPsi = last + remainder(dPsi - last, 2.0 * M_PI);
     1898      }
     1899      _dPsiLast[key] = QPair<double, bncTime>(dPsi, corr->_time);
    18881900      dC_att = corr->_lambdaIF * dPsi / (2.0 * M_PI);
    1889       if (fabs(dC_att) > 1.e-6) fprintf(stderr, "DCATT %s %s %s %s %.4f %.2f\n", corr->_time.timestr().c_str(), corr->_acName.toLatin1().data(), masterCorr[prn]->_acName.toLatin1().data(), prn.toLatin1().data(), dC_att, dPsi*180/M_PI);
     1901      if (getenv("DCATT") && prn.startsWith(getenv("DCATT"))) fprintf(stderr, "DCATT %s %s %s %s %.4f %.2f %.2f %.2f\n", corr->_time.timestr().c_str(), corr->_acName.toLatin1().data(), masterCorr[prn]->_acName.toLatin1().data(), prn.toLatin1().data(), dC_att, dPsi*180/M_PI, corr->_attYaw*180/M_PI, masterCorr[prn]->_attYaw*180/M_PI); // TEMPORARY
    18901902    }
    18911903    double dC_apriori_corr = dC_orb + dC_att;
  • trunk/BNC/src/combination/bnccomb.h

    r11105 r11110  
    297297  QMap<char, QVector<cmbParam*>>              _params;
    298298  QMap<QString, QMap<t_prn, t_orbCorr> >     _orbCorrections;
     299  QMap<QString, QPair<double, bncTime> >      _dPsiLast; // yaw difference to the master orbit AC per AC, master and satellite, unwrapped over time
    299300  QMap<QString, QMap<t_prn, t_orbCorr> >     _orbCorrectionsPrev; // latest orbit of the previous IOD per AC and satellite
    300301  QMap<QString, QMap<t_prn, t_satCodeBias>>  _satCodeBiases;
Note: See TracChangeset for help on using the changeset viewer.