source: ntrip/trunk/BNC/src/orbComp/sp3Comp.cpp@ 11158

Last change on this file since 11158 was 11158, checked in by stuerze, 7 days ago

deprecated functions exchanged libs updated

File size: 19.2 KB
Line 
1// Part of BNC, a utility for retrieving decoding and
2// converting GNSS data streams from NTRIP broadcasters.
3//
4// Copyright (C) 2007
5// German Federal Agency for Cartography and Geodesy (BKG)
6// http://bkg.bund.de
7// Czech Technical University Prague, Department of Geodesy
8// http://www.fsv.cvut.cz
9//
10// Email: euref-ip@bkg.bund.de
11//
12// This program is free software; you can redistribute it and/or
13// modify it under the terms of the GNU General Public License
14// as published by the Free Software Foundation, version 2.
15//
16// This program is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with this program; if not, write to the Free Software
23// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25/* -------------------------------------------------------------------------
26 * BKG NTRIP Client
27 * -------------------------------------------------------------------------
28 *
29 * Class: t_sp3Comp
30 *
31 * Purpose: Compare SP3 Files
32 *
33 * Author: L. Mervart
34 *
35 * Created: 24-Nov-2014
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <QRegularExpression>
42#include <iostream>
43#include <iomanip>
44#include "sp3Comp.h"
45#include "bnccore.h"
46#include "bncsettings.h"
47#include "bncutils.h"
48#include "bncsp3.h"
49
50using namespace std;
51
52// Constructor
53////////////////////////////////////////////////////////////////////////////
54t_sp3Comp::t_sp3Comp(QObject* parent) : QThread(parent) {
55
56 bncSettings settings;
57 _sp3FileNames = settings.value("sp3CompFile").toString().split(QRegularExpression("[ ,]"), Qt::SkipEmptyParts);
58 for (int ii = 0; ii < _sp3FileNames.size(); ii++) {
59 expandEnvVar(_sp3FileNames[ii]);
60 }
61 _logFileName = settings.value("sp3CompOutLogFile").toString(); expandEnvVar(_logFileName);
62 _logFile = 0;
63 _log = 0;
64
65 _excludeSats = settings.value("sp3CompExclude").toString().split(QRegularExpression("[ ,]"), Qt::SkipEmptyParts);
66
67 _summaryOnly = (Qt::CheckState(settings.value("sp3CompSummaryOnly").toInt()) == Qt::Checked);
68}
69
70// Destructor
71////////////////////////////////////////////////////////////////////////////
72t_sp3Comp::~t_sp3Comp() {
73 delete _log;
74 delete _logFile;
75}
76
77//
78////////////////////////////////////////////////////////////////////////////
79void t_sp3Comp::run() {
80
81 // Open Log File
82 // -------------
83 _logFile = new QFile(_logFileName);
84 if (_logFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
85 _log = new QTextStream();
86 _log->setDevice(_logFile);
87 }
88 if (!_log) {
89 *_log << "ERROR: SP3Comp requires logfile specification" << "\n";
90 goto exit;
91 }
92
93 for (int ii = 0; ii < _sp3FileNames.size(); ii++) {
94 *_log << "! SP3 File " << ii+1 << ": " << _sp3FileNames[ii] << "\n";
95 }
96 if (_sp3FileNames.size() != 2) {
97 *_log << "ERROR: sp3Comp requires two input SP3 files" << "\n";
98 goto end;
99 }
100
101 try {
102 ostringstream msg;
103 compare(msg);
104 *_log << msg.str().c_str();
105 }
106 catch (const string& error) {
107 *_log << "ERROR: " << error.c_str() << "\n";
108 }
109 catch (const char* error) {
110 *_log << "ERROR: " << error << "\n";
111 }
112 catch (Exception& exc) {
113 *_log << "ERROR: " << exc.what() << "\n";
114 }
115 catch (std::exception& exc) {
116 *_log << "ERROR: " << exc.what() << "\n";
117 }
118 catch (QString error) {
119 *_log << "ERROR: " << error << "\n";
120 }
121 catch (...) {
122 *_log << "ERROR: " << "unknown exception" << "\n";
123 }
124
125 // Exit (thread)
126 // -------------
127 end:
128 _log->flush();
129 exit:
130 // do nothing if no logfile available
131
132 if (BNC_CORE->mode() != t_bncCore::interactive) {
133 qApp->exit(7);
134 msleep(100); //sleep 0.1 sec
135 }
136 else {
137 emit finished();
138 deleteLater();
139 }
140}
141
142// Satellite Index in clkSats set
143////////////////////////////////////////////////////////////////////////////////
144int t_sp3Comp::satIndex(const set<t_prn>& clkSats, const t_prn& prn) const {
145 int ret = 0;
146 for (set<t_prn>::const_iterator it = clkSats.begin(); it != clkSats.end(); it++) {
147 if ( *it == prn) {
148 return ret;
149 }
150 ++ret;
151 }
152 return -1;
153}
154
155// Estimate Clock Offsets
156////////////////////////////////////////////////////////////////////////////////
157void t_sp3Comp::processClocks(const set<t_prn>& clkSats, const vector<t_epoch*>& epochsIn,
158 map<string, t_stat>& stat) const {
159
160 if (clkSats.size() == 0) {
161 return;
162 }
163
164 vector<t_epoch*> epochs;
165 for (unsigned ii = 0; ii < epochsIn.size(); ii++) {
166 unsigned numSatOK = 0;
167 std::map<t_prn, double>::const_iterator it;
168 for (it = epochsIn[ii]->_dc.begin(); it != epochsIn[ii]->_dc.end(); it++) {
169 if (satIndex(clkSats, it->first) != -1) {
170 numSatOK += 1;
171 }
172 }
173 if (numSatOK > 0) {
174 epochs.push_back(epochsIn[ii]);
175 }
176 }
177 if (epochs.size() == 0) {
178 return;
179 }
180
181 // Least-squares adjustment of an epoch-specific offset (common to all
182 // satellites) and a satellite-specific offset (common to all epochs), the
183 // sum of the satellite offsets constrained to zero (weight 1). The normal
184 // equations of the epoch offsets are diagonal, so these offsets are
185 // eliminated (Schur complement) and only the satellite offsets are solved
186 // for; the epoch offsets follow from them. The solution is identical to
187 // that of the full system, but the effort grows only linearly with the
188 // number of epochs (before: cubic).
189 // -----------------------------------------------------------------------
190 int nEpo = epochs.size();
191 int nSat = clkSats.size();
192 int nPar = nEpo + nSat;
193
194 vector<vector<int> > satIdx(nEpo); // satellites (index) of each epoch
195 ColumnVector nEp(nEpo); nEp = 0.0; // number of satellites per epoch
196 ColumnVector bEp(nEpo); bEp = 0.0; // sum of the differences per epoch
197 SymmetricMatrix NS(nSat); NS = 1.0; // regularization: sum of satellite offsets
198 ColumnVector bS(nSat); bS = 0.0;
199 for (int ie = 0; ie < nEpo; ie++) {
200 const map<t_prn, double>& dc = epochs[ie]->_dc;
201 for (map<t_prn, double>::const_iterator it = dc.begin(); it != dc.end(); it++) {
202 int is = satIndex(clkSats, it->first);
203 if (is != -1) {
204 satIdx[ie].push_back(is);
205 nEp[ie] += 1.0;
206 bEp[ie] += it->second;
207 NS(is+1, is+1) += 1.0;
208 bS[is] += it->second;
209 }
210 }
211 }
212
213 // Eliminate the epoch offsets
214 // ---------------------------
215 for (int ie = 0; ie < nEpo; ie++) {
216 const vector<int>& idx = satIdx[ie];
217 for (unsigned ia = 0; ia < idx.size(); ia++) {
218 bS[idx[ia]] -= bEp[ie] / nEp[ie];
219 for (unsigned ib = 0; ib <= ia; ib++) {
220 NS(idx[ia]+1, idx[ib]+1) -= 1.0 / nEp[ie];
221 }
222 }
223 }
224
225 // Estimate Parameters (epoch offsets first, then satellite offsets)
226 // ---------------------------------------------------------------
227 ColumnVector yy = NS.i() * bS;
228 ColumnVector xx(nPar);
229 for (int ie = 0; ie < nEpo; ie++) {
230 double sum = bEp[ie];
231 for (unsigned ia = 0; ia < satIdx[ie].size(); ia++) {
232 sum -= yy[satIdx[ie][ia]];
233 }
234 xx[ie] = sum / nEp[ie];
235 }
236 for (int is = 0; is < nSat; is++) {
237 xx[nEpo + is] = yy[is];
238 }
239
240 // Compute clock residuals
241 // -----------------------
242 for (unsigned ie = 0; ie < epochs.size(); ie++) {
243 map<t_prn, double>& dc = epochs[ie]->_dc;
244 map<t_prn, double>& dcRed = epochs[ie]->_dcRed;
245 const map<t_prn, ColumnVector>& dr = epochs[ie]->_dr;
246 for (map<t_prn, double>::iterator it = dc.begin(); it != dc.end(); it++) {
247 const t_prn& prn = it->first;
248 stringstream all; all << prn.system() << 99;
249 if (satIndex(clkSats, prn) != -1) {
250 int index = epochs.size() + satIndex(clkSats, prn);
251 dc[prn] = it->second - xx[ie] - xx[index];
252 stat[prn.toString()]._offset = xx[index];
253 if (dr.find(prn) != dr.end()){
254 // signal-in-space difference: radial minus clock, only the
255 // epoch-wise clock offset (datum) removed, i.e. including the
256 // satellite-specific clock offset
257 epochs[ie]->_sis[prn] = dr.find(prn)->second[0] - (dc[prn] + xx[index]);
258 dcRed[prn] = dc[prn] - dr.find(prn)->second[0]; // clock minus radial component
259 stat[prn.toString()]._dcRedMean += dcRed[prn];
260 stat[all.str() ]._dcRedMean += dcRed[prn];
261 stat[prn.toString()]._nc += 1;
262 stat[all.str() ]._nc += 1;
263 }
264 }
265 }
266 }
267
268 // Compute Clock Mean
269 // ------------------
270 for (map<string, t_stat>::iterator it = stat.begin(); it != stat.end(); it++) {
271 t_stat& stat = it->second;
272 if (stat._nc > 0) {
273 stat._dcRedMean = stat._dcRedMean / stat._nc;
274 }
275 }
276}
277
278// Main Routine
279////////////////////////////////////////////////////////////////////////////////
280void t_sp3Comp::compare(ostringstream& out) const {
281
282 // Synchronize reading of two sp3 files
283 // ------------------------------------
284 bncSP3 in1(_sp3FileNames[0]); in1.nextEpoch();
285 bncSP3 in2(_sp3FileNames[1]); in2.nextEpoch();
286
287 vector<t_epoch*> epochs;
288 while (in1.currEpoch() && in2.currEpoch()) {
289 bncTime t1 = in1.currEpoch()->_tt;
290 bncTime t2 = in2.currEpoch()->_tt;
291 if (t1 < t2) {
292 in1.nextEpoch();
293 }
294 else if (t1 > t2) {
295 in2.nextEpoch();
296 }
297 else if (t1 == t2) {
298 t_epoch* epo = new t_epoch; epo->_tt = t1;
299 bool epochOK = false;
300 for (int i1 = 0; i1 < in1.currEpoch()->_sp3Sat.size(); i1++) {
301 bncSP3::t_sp3Sat* sat1 = in1.currEpoch()->_sp3Sat[i1];
302 for (int i2 = 0; i2 < in2.currEpoch()->_sp3Sat.size(); i2++) {
303 bncSP3::t_sp3Sat* sat2 = in2.currEpoch()->_sp3Sat[i2];
304 if (sat1->_prn == sat2->_prn &&
305 sat1->_clkValid && sat2->_clkValid) {
306 epochOK = true;
307 epo->_dr[sat1->_prn] = sat1->_xyz - sat2->_xyz;
308 epo->_xyz[sat1->_prn] = sat1->_xyz;
309 epo->_dc[sat1->_prn] = sat1->_clk - sat2->_clk;
310 }
311 }
312 }
313 if (epochOK) {
314 epochs.push_back(epo);
315 }
316 else {
317 delete epo;
318 }
319 in1.nextEpoch();
320 in2.nextEpoch();
321 }
322 }
323
324 // Transform xyz into radial, along-track, and out-of-plane
325 // --------------------------------------------------------
326 if (epochs.size() < 2) {
327 throw "t_sp3Comp: not enough common epochs";
328 }
329
330 set<t_prn> clkSatsAll;
331
332 for (unsigned ie = 0; ie < epochs.size(); ie++) {
333 t_epoch* epoch = epochs[ie];
334 t_epoch* epoch2 = 0;
335 if (ie == 0) {
336 epoch2 = epochs[ie+1];
337 }
338 else {
339 epoch2 = epochs[ie-1];
340 }
341 double dt = epoch->_tt - epoch2->_tt;
342 map<t_prn, ColumnVector>& dr = epoch->_dr;
343 map<t_prn, ColumnVector>& xyz = epoch->_xyz;
344 map<t_prn, ColumnVector>& xyz2 = epoch2->_xyz;
345 QList<t_prn> satEraseList;
346 for (map<t_prn, ColumnVector>::const_iterator it = dr.begin(); it != dr.end(); it++) {
347 const t_prn& prn = it->first;
348 if (xyz2.find(prn) != xyz2.end()) {
349 const ColumnVector dx = dr[prn];
350 const ColumnVector& x1 = xyz[prn];
351 const ColumnVector& x2 = xyz2[prn];
352 ColumnVector vel = (x1 - x2) / dt;
353 XYZ_to_RSW(x1, vel, dx, dr[prn]);
354 if (epoch->_dc.find(prn) != epoch->_dc.end()) {
355 clkSatsAll.insert(prn);
356 }
357 }
358 else {
359 satEraseList << it->first;
360 }
361 }
362 for (QList<t_prn>::const_iterator it = satEraseList.begin(); it != satEraseList.end(); it++) {
363 epoch->_dc.erase(*it);
364 epoch->_dr.erase(*it);
365 }
366 }
367
368 map<string, t_stat> stat;
369
370 // Estimate Clock Offsets
371 // ----------------------
372 string systems;
373 for (set<t_prn>::const_iterator it = clkSatsAll.begin(); it != clkSatsAll.end(); it++) {
374 if (systems.find(it->system()) == string::npos) {
375 systems += it->system();
376 }
377 }
378 for (unsigned iSys = 0; iSys < systems.size(); iSys++) {
379 char system = systems[iSys];
380 set<t_prn> clkSats;
381 for (set<t_prn>::const_iterator it = clkSatsAll.begin(); it != clkSatsAll.end(); it++) {
382 if (it->system() == system && !excludeSat(*it)) {
383 clkSats.insert(*it);
384 }
385 }
386 processClocks(clkSats, epochs, stat);
387 }
388
389 // Print epoch-wise Clock Residuals
390 // --------------------------------
391 out.setf(ios::fixed);
392 if (!_summaryOnly) {
393 out << "!\n! Clock residuals and orbit differences in [m]\n"
394 "! ----------------------------------------------------------------------------\n";
395 out << "!\n! Epoch PRN radial along out clk clkRed iPRN SIS"
396 "\n! -------------------------------------------------------------------------------------\n";
397 }
398 for (unsigned ii = 0; ii < epochs.size(); ii++) {
399 const t_epoch* epo = epochs[ii];
400 const map<t_prn, ColumnVector>& dr = epochs[ii]->_dr;
401 const map<t_prn, double>& dc = epochs[ii]->_dc;
402 const map<t_prn, double>& dcRed = epochs[ii]->_dcRed;
403 const map<t_prn, double>& sis = epochs[ii]->_sis;
404 for (map<t_prn, ColumnVector>::const_iterator it = dr.begin(); it != dr.end(); it++) {
405 const t_prn& prn = it->first;
406 stringstream all; all << prn.system() << 99;
407 if (!excludeSat(prn)) {
408 const ColumnVector& rao = it->second;
409 if (!_summaryOnly) {
410 out << setprecision(6) << string(epo->_tt) << ' ' << prn.toString() << ' '
411 << setw(7) << setprecision(4) << rao[0] << ' '
412 << setw(7) << setprecision(4) << rao[1] << ' '
413 << setw(7) << setprecision(4) << rao[2] << " ";
414 }
415 stat[prn.toString()]._rao += SP(rao, rao); // Schur product
416 stat[all.str() ]._rao += SP(rao, rao);
417 stat[prn.toString()]._nr += 1;
418 stat[all.str() ]._nr += 1;
419 if (dc.find(prn) != dc.end() && dcRed.find(prn) != dc.end()) {
420 double clkRes = dc.find(prn)->second;
421 double clkResRed = dcRed.find(prn)->second;
422 if (!_summaryOnly) {
423 out << setw(7) << setprecision(4) << clkRes << ' '
424 << setw(7) << setprecision(4) << clkResRed;
425 }
426 stat[prn.toString()]._dcRMS += clkRes * clkRes;
427 stat[all.str() ]._dcRMS += clkRes * clkRes;
428 stat[prn.toString()]._dcRedRMS += clkResRed * clkResRed;
429 stat[all.str() ]._dcRedRMS += clkResRed * clkResRed;
430 stat[prn.toString()]._dcRedSig += (clkResRed - stat[prn.toString()]._dcRedMean) *
431 (clkResRed - stat[prn.toString()]._dcRedMean);
432 stat[all.str() ]._dcRedSig += (clkResRed - stat[all.str() ]._dcRedMean) *
433 (clkResRed - stat[all.str() ]._dcRedMean);
434 }
435 else {
436 if (!_summaryOnly) {
437 out << " . . ";
438 }
439 }
440 if (!_summaryOnly) {
441 out << " " << setw(2) << int(prn);
442 }
443 if (sis.find(prn) != sis.end()) {
444 double sisVal = sis.find(prn)->second;
445 stat[prn.toString()]._sisSum += sisVal;
446 stat[all.str() ]._sisSum += sisVal;
447 stat[prn.toString()]._sisSqr += sisVal * sisVal;
448 stat[all.str() ]._sisSqr += sisVal * sisVal;
449 stat[prn.toString()]._ns += 1;
450 stat[all.str() ]._ns += 1;
451 if (!_summaryOnly) {
452 out << " " << setw(7) << setprecision(4) << sisVal;
453 }
454 }
455 if (!_summaryOnly) {
456 out << "\n";
457 }
458 }
459 }
460 delete epo;
461 }
462
463 // Print Summary
464 // -------------
465 out << "!\n! Summary";
466 out << "\n! -----------------------------------------------------------------------------------------------------------------\n";
467 out << "!\n! PRN radialRMS alongRMS outRMS 3DRMS nOrb clkRMS clkRedRMS clkRedSig nClk Offset SISRMS SISSig"
468 "\n! [mm] [mm] [mm] [mm] [-] [ns] [ns] [ns] [-] [ns] [mm] [mm]"
469 "\n! -------------------------------------------------------------------------------------------------------------------------------------\n";
470 // SIS standard deviation of a system: pooled about the satellite means
471 map<char, double> sisVarSys;
472 for (map<string, t_stat>::const_iterator it = stat.begin(); it != stat.end(); it++) {
473 const t_stat& st = it->second;
474 stringstream all; all << it->first[0] << 99;
475 if (it->first != all.str() && st._ns > 0) {
476 sisVarSys[it->first[0]] += st._sisSqr - st._sisSum * st._sisSum / st._ns;
477 }
478 }
479 for (map<string, t_stat>::iterator it = stat.begin(); it != stat.end(); it++) {
480 const string& prn = it->first;
481 t_stat& stat = it->second;
482 char sys = prn[0];
483 stringstream all; all << sys << 99;
484 if (stat._nr > 0) {
485 stat._rao[0] = sqrt(stat._rao[0] / stat._nr);
486 stat._rao[1] = sqrt(stat._rao[1] / stat._nr);
487 stat._rao[2] = sqrt(stat._rao[2] / stat._nr);
488 stat._rao3DRMS = stat._rao.NormFrobenius();
489 // orbit values in millimeter
490 if (prn != all.str()) {
491 (_summaryOnly) ? out << " " << prn << ' ':
492 out << "! " << prn << ' ';
493 }
494 else {
495 QString sys; sys = all.str()[0];
496 (_summaryOnly) ? out << " " << QString("%1").arg(sys,3,' ').toStdString().c_str() << " ":
497 out << "! " << QString("%1").arg(sys,3,' ').toStdString().c_str() << " ";
498 }
499 out << setw(10) << setprecision(1) << stat._rao[0] * 1e3 << ' '
500 << setw(10) << setprecision(1) << stat._rao[1] * 1e3 << ' '
501 << setw(10) << setprecision(1) << stat._rao[2] * 1e3 << ' '
502 << setw(10) << setprecision(1) << stat._rao3DRMS * 1e3 << ' '
503 << setw( 7) << stat._nr << " ";
504 // clock values in nano seconds
505 if (stat._nc > 0) {
506 stat._dcRMS = sqrt(stat._dcRMS / stat._nc);
507 stat._dcRedRMS = sqrt(stat._dcRedRMS / stat._nc);
508 stat._dcRedSig = sqrt(stat._dcRedSig / stat._nc);
509 out << setw(10) << setprecision(2) << stat._dcRMS / t_CST::c * 1e9 << ' '
510 << setw(10) << setprecision(2) << stat._dcRedRMS / t_CST::c * 1e9 << ' '
511 << setw(10) << setprecision(2) << stat._dcRedSig / t_CST::c * 1e9 << ' '
512 << setw( 9) << stat._nc << " ";
513 if (prn != "G99" && prn != "R99" && prn != "E99" && prn != "C98" && prn != "C99") {
514 out << setw( 9) << setprecision(2) << stat._offset / t_CST::c * 1e9;
515 }
516 else {
517 out << setw( 9) << " ";
518 }
519 }
520 if (stat._ns > 0) {
521 double var = (prn == all.str()) ? sisVarSys[sys] / stat._ns
522 : stat._sisSqr / stat._ns - pow(stat._sisSum / stat._ns, 2);
523 out << setw(11) << setprecision(1) << sqrt(stat._sisSqr / stat._ns) * 1e3 << ' '
524 << setw( 9) << setprecision(1) << sqrt(max(0.0, var)) * 1e3;
525 }
526 out << "\n";
527 }
528 }
529}
530
531//
532////////////////////////////////////////////////////////////////////////////
533bool t_sp3Comp::excludeSat(const t_prn& prn) const {
534 QStringListIterator it(_excludeSats);
535 while (it.hasNext()) {
536 string prnStr = it.next().toLatin1().data();
537 if (prnStr == prn.toString() || // prn
538 prnStr == prn.toString().substr(0,1)) { // sys
539 return true;
540 }
541 }
542 return false;
543}
544
Note: See TracBrowser for help on using the repository browser.