File: gnssBiasClockAlignment.cpp

package info (click to toggle)
groops 0%2Bgit20250907%2Bds-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 11,140 kB
  • sloc: cpp: 135,607; fortran: 1,603; makefile: 20
file content (266 lines) | stat: -rw-r--r-- 12,005 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/***********************************************/
/**
* @file gnssBiasClockAlignment.cpp
*
* @brief Align GNSS transmitter clocks to reference clocks and adjust related receiver signal biases as well as GLONASS transmitter biases.
*
* @author Sebastian Strasser
* @date 2019-09-03
*/
/***********************************************/

// Latex documentation
#define DOCSTRING docstring
static const char *docstring = R"(
This program can be used to absolutely align GNSS transmitter clocks to reference clocks (i.e. broadcast clocks).
Each 'group' of \config{transmitter}s, usually a system like GPS or Galileo, is aligned individually by a constant shift over all transmitters.
If \config{alignClocksByFreqNo} is set, GLONASS transmitters will be divided by frequency number into groups of nominally two transmitters.
The offset between clocks and reference clocks will be shifted into receiver code biases, if \config{receiver} is provided."

By setting \config{alignFreqNoBiasesAtReceiver} and providing \config{receiver}, this program can further align GLONASS transmitter signal
biases so that the differences between frequency number-dependent receiver signal biases are minimal, which helps if PPP users don't set
up individual signal biases per frequency number at the receiver. Alignment is done by computing signal bias residuals to the mean over all
frequency numbers of a signal type at each receiver and then computing the means over all receivers for each frequency number and shifting
those from the receiver signal biases to the transmitter signal biases. Internal consistency of the biases is not affected by this.

If you only want to align GLONASS frequency numbers, provide the same clocks in
\configFile{inputfileClock}{instrument} and \configFile{inputfileReferenceClock}{instrument}.
)";

/***********************************************/

#include "programs/program.h"
#include "files/fileMatrix.h"
#include "files/filePlatform.h"
#include "files/fileGnssSignalBias.h"
#include "files/fileInstrument.h"

/***** CLASS ***********************************/

/** @brief Align GNSS transmitter clocks to reference clocks and adjust related receiver signal biases as well as GLONASS transmitter biases.
* @ingroup programsGroup */
class GnssBiasClockAlignment
{
public:
  class Transmitter
  {
  public:
    FileName outNameClock, outNameBias, inNameClock, inNameReferenceClock, inNameBias, inNameTransmitterInfo;
    MiscValueArc clockArc, referenceClockArc;
    GnssSignalBias biases;
    Platform platform;
  };

  class Receiver
  {
  public:
    FileName outNameBias, inNameBias;
    GnssSignalBias biases;
  };

  void run(Config &config, Parallel::CommunicatorPtr comm);
};

GROOPS_REGISTER_PROGRAM(GnssBiasClockAlignment, SINGLEPROCESS, "Align GNSS transmitter clocks to reference clocks and adjust related receiver signal biases as well as GLONASS transmitter biases.", Gnss, Instrument)

/***********************************************/

template<> Bool readConfig(Config &config, const std::string &name, GnssBiasClockAlignment::Transmitter &var, Config::Appearance mustSet, const std::string &defaultValue, const std::string &annotation)
{
  if(!readConfigSequence(config, name, mustSet, defaultValue, annotation))
    return FALSE;
  readConfig(config, "outputfileClock",          var.outNameClock,          Config::MUSTSET,  "", "aligned clock instrument file");
  readConfig(config, "outputfileSignalBias",     var.outNameBias,           Config::OPTIONAL, "", "(GLONASS only) aligned signal bias file");
  readConfig(config, "inputfileClock",           var.inNameClock,           Config::MUSTSET,  "", "clock instrument file");
  readConfig(config, "inputfileReferenceClock",  var.inNameReferenceClock,  Config::MUSTSET,  "", "reference clock instrument file");
  readConfig(config, "inputfileSignalBias",      var.inNameBias,            Config::OPTIONAL, "", "(GLONASS only) signal bias file");
  readConfig(config, "inputfileTransmitterInfo", var.inNameTransmitterInfo, Config::MUSTSET,  "{groopsDataDir}/gnss/transmitter/transmitterInfo/igs/igs20/transmitterInfo_igs20.{prn}.xml", "transmitter platform file");
  endSequence(config);
  return TRUE;
}

/***********************************************/

template<> Bool readConfig(Config &config, const std::string &name, GnssBiasClockAlignment::Receiver &var, Config::Appearance mustSet, const std::string &defaultValue, const std::string &annotation)
{
  if(!readConfigSequence(config, name, mustSet, defaultValue, annotation))
    return FALSE;
  readConfig(config, "outputfileSignalBias",    var.outNameBias,          Config::MUSTSET, "", "aligned signal bias file");
  readConfig(config, "inputfileSignalBias",     var.inNameBias,           Config::MUSTSET, "", "signal bias file");
  endSequence(config);
  return TRUE;
}

/***********************************************/

void GnssBiasClockAlignment::run(Config &config, Parallel::CommunicatorPtr /*comm*/)
{
  try
  {
    std::vector<Transmitter> transmitters;
    std::vector<Receiver> receivers;
    Bool alignClocksByFreqNo, alignFreqNoBiasesAtReceiver;

    readConfig(config, "transmitter",                 transmitters,                Config::MUSTSET,  "",  "one element per satellite");
    readConfig(config, "receiver",                    receivers,                   Config::OPTIONAL, "",  "one element per station");
    readConfig(config, "alignClocksByFreqNo",         alignClocksByFreqNo,         Config::DEFAULT,  "1", "align clocks for each GLONASS frequency number separately");
    readConfig(config, "alignFreqNoBiasesAtReceiver", alignFreqNoBiasesAtReceiver, Config::DEFAULT,  "1", "align frequency number-dependent code biases for each receiver");
    if(isCreateSchema(config)) return;

    logStatus<<"read transmitter input files"<<Log::endl;
    std::map<GnssType, std::vector<UInt>> group2DataIds;
    Single::forEach(transmitters.size(), [&](UInt i)
    {
      Transmitter *trans = &transmitters.at(i);
      try
      {
        trans->clockArc          = InstrumentFile::read(trans->inNameClock);
        trans->referenceClockArc = InstrumentFile::read(trans->inNameReferenceClock);
        if(!trans->inNameBias.empty())
          readFileGnssSignalBias(trans->inNameBias, trans->biases);
        readFilePlatform(trans->inNameTransmitterInfo, trans->platform);
      }
      catch(std::exception &e)
      {
        logWarning<<e.what()<<" continue..."<<Log::endl;
        return;
      }

      auto recv = trans->platform.findEquipment<PlatformGnssReceiver>(trans->clockArc.at(trans->clockArc.size()/2).time);
      if(!recv)
      {
        logWarning<<"no receiver info found in <"<<trans->inNameTransmitterInfo<<">, skipping satellite..."<<Log::endl;
        return;
      }

      GnssType group("***"s+recv->serial.at(0));
      if(alignClocksByFreqNo && group == GnssType::GLONASS && !recv->version.empty())
        group.setFrequencyNumber(std::stoi(recv->version));
      group2DataIds[group].push_back(i);
    });

    if(receivers.size())
    {
      logStatus<<"read receiver input files"<<Log::endl;
      Single::forEach(receivers.size(), [&](UInt i)
      {
        try
        {
          readFileGnssSignalBias(receivers.at(i).inNameBias, receivers.at(i).biases);
        }
        catch(std::exception &e)
        {
          logWarning<<e.what()<<" continue..."<<Log::endl;
        }
      });
    }

    // shift mean of transmitter clock diffs per group to receiver code biases
    logInfo<<"clock shifts per group:"<<Log::endl;
    for(const auto &group : group2DataIds)
    {
      // mean over all satellite clocks in group
      std::vector<Double> clockDiffs;
      for(UInt i : group.second)
        for(UInt idEpoch = 0; idEpoch < transmitters.at(i).clockArc.size(); idEpoch++)
          clockDiffs.push_back(transmitters.at(i).clockArc.at(idEpoch).value - transmitters.at(i).referenceClockArc.at(idEpoch).value);
      const Double mean = std::accumulate(clockDiffs.begin(), clockDiffs.end(), 0.0)/clockDiffs.size();
      logInfo<<group.first.str()<<group.second.size()%" (%i sat)"s<<" = "<<(mean*1e9)%"%10.3f ns"s<<" = "<<(mean*LIGHT_VELOCITY)%"%10.3f m"s<<Log::endl;

      // update transmitter clocks
      for(UInt i : group.second)
        for(UInt idEpoch = 0; idEpoch < transmitters.at(i).clockArc.size(); idEpoch++)
          transmitters.at(i).clockArc.at(idEpoch).value -= mean;

      // update receiver code biases
      for(auto &&rec : receivers)
        for(UInt i = 0; i < rec.biases.types.size(); i++)
          if(rec.biases.types.at(i) == group.first + GnssType::RANGE)
            rec.biases.biases.at(i) -= mean * LIGHT_VELOCITY;
    }

    if(alignFreqNoBiasesAtReceiver)
    {
      if(!receivers.size())
        throw(Exception("no receivers found, cannot align frequency number-dependent biases"));

      std::map<GnssType, std::vector<Double>> bias2Residuals;

      // compute residuals of receiver biases to mean bias over all GLONASS frequency numbers per station
      for(auto &&rec : receivers)
      {
        // GLONASS signal bias types independent of frequency number
        std::set<GnssType> types;
        for(const auto &type : rec.biases.types)
          if(type == GnssType::GLONASS)
            types.insert(type & ~GnssType::FREQ_NO & (type == GnssType::PHASE ? ~GnssType::ATTRIBUTE : GnssType::ALL));

        for(const auto &type : types)
        {
          // mean over all frequency numbers for this type
          std::vector<Double> values;
          for(UInt i = 0; i < rec.biases.types.size(); i++)
            if(rec.biases.types.at(i) == type)
              values.push_back(rec.biases.biases.at(i));
          const Double mean = ::mean(Vector(values));

          // bias residuals per frequency number for this type
          for(UInt i = 0; i < rec.biases.types.size(); i++)
            if(rec.biases.types.at(i) == type)
            {
              GnssType t = rec.biases.types.at(i) & (type == GnssType::PHASE ? ~GnssType::ATTRIBUTE : GnssType::ALL);
              bias2Residuals[t].push_back(rec.biases.biases.at(i) - mean);
            }
        }
      }

      // shift mean of receiver bias residuals over all stations to transmitter bias per type and frequency number
      logInfo<<"bias shifts per frequency number and type:"<<Log::endl;
      for(const auto &bias : bias2Residuals)
      {
        const GnssType type = bias.first;
        const Double mean = ::mean(Vector(bias.second));
        logInfo<<" "<<type.str()<<" = "<<mean%"%10.3f m"s<<Log::endl;

        // remove mean from receiver bias of all stations
        for(auto &&rec : receivers)
          for(UInt i = 0; i < rec.biases.types.size(); i++)
            if(rec.biases.types.at(i) == type)
              rec.biases.biases.at(i) -= mean;

        // add mean to transmitter bias
        for(auto &trans : transmitters)
          for(UInt i = 0; i < trans.biases.types.size(); i++)
            if(trans.biases.types.at(i) == type)
              trans.biases.biases.at(i) += mean;
      }
    }

    logStatus<<"writing aligned clock files <"<<transmitters.at(0).outNameClock<<">"<<Log::endl;
    for(const auto &trans : transmitters)
      if(trans.clockArc.size())
        InstrumentFile::write(trans.outNameClock, trans.clockArc);

    if(receivers.size())
    {
      logStatus<<"writing aligned receiver signal bias files <"<<receivers.at(0).outNameBias<<">"<<Log::endl;
      for(const auto &rec : receivers)
        if(rec.biases.types.size())
          writeFileGnssSignalBias(rec.outNameBias, rec.biases);

      if(alignFreqNoBiasesAtReceiver)
      {
        logStatus<<"writing aligned transmitter signal bias files <"<<transmitters.at(0).outNameBias<<">"<<Log::endl;
        for(const auto &trans : transmitters)
          if(trans.outNameBias)
            writeFileGnssSignalBias(trans.outNameBias, trans.biases);
      }
    }
  }
  catch(std::exception &e)
  {
    GROOPS_RETHROW(e)
  }
}

/***********************************************/