source: ntrip/trunk/BNC/src/rinex/reqcedit.cpp@ 11057

Last change on this file since 11057 was 11057, checked in by stuerze, 6 days ago

bug fixes resulting from overall tests

File size: 30.8 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_reqcEdit
30 *
31 * Purpose: Edit/Concatenate RINEX Files
32 *
33 * Author: L. Mervart
34 *
35 * Created: 11-Apr-2012
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <iostream>
42#include "reqcedit.h"
43#include "bnccore.h"
44#include "bncsettings.h"
45#include "bncutils.h"
46#include "rnxobsfile.h"
47#include "rnxnavfile.h"
48
49using namespace std;
50
51// Constructor
52////////////////////////////////////////////////////////////////////////////
53t_reqcEdit::t_reqcEdit(QObject* parent) : QThread(parent) {
54
55 bncSettings settings;
56
57 _logFileName = settings.value("reqcOutLogFile").toString(); expandEnvVar(_logFileName);
58 _logFile = 0;
59 _logStream = 0;
60 _obsFileNames = settings.value("reqcObsFile").toString().split(",", Qt::SkipEmptyParts);
61 _outObsFileName = settings.value("reqcOutObsFile").toString();
62 _navFileNames = settings.value("reqcNavFile").toString().split(",", Qt::SkipEmptyParts);
63 _outNavFileName = settings.value("reqcOutNavFile").toString();
64 int version = settings.value("reqcRnxVersion").toInt();
65 if (version == 2) {
66 _rnxVersion = defaultRnxObsVersion2;
67 }
68 else if (version == 3) {
69 _rnxVersion = defaultRnxObsVersion3;
70 }
71 else if (version == 4) {
72 _rnxVersion = defaultRnxObsVersion4;
73 }
74 _samplingRate = settings.value("reqcSampling").toString().split("sec").first().toDouble();
75 _minEle = settings.value("reqcMinEle").toDouble();
76 _begTime = bncTime(settings.value("reqcStartDateTime").toString().toLatin1().data());
77 _endTime = bncTime(settings.value("reqcEndDateTime").toString().toLatin1().data());
78
79 _checkEph = false;
80
81}
82
83// Destructor
84////////////////////////////////////////////////////////////////////////////
85t_reqcEdit::~t_reqcEdit() {
86 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
87 delete _rnxObsFiles[ii];
88 }
89 for (int ii = 0; ii < _ephs.size(); ii++) {
90 delete _ephs[ii];
91 }
92 delete _logStream; _logStream = 0;
93 delete _logFile; _logFile = 0;
94}
95
96//
97////////////////////////////////////////////////////////////////////////////
98void t_reqcEdit::run() {
99
100 // Open Log File
101 // -------------
102 if (!_logFileName.isEmpty()) {
103 _logFile = new QFile(_logFileName);
104 if (_logFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
105 _logStream = new QTextStream();
106 _logStream->setDevice(_logFile);
107 }
108 }
109
110 // Log File Header
111 // ---------------
112 if (_logStream) {
113 *_logStream << QByteArray(78, '-') << Qt::endl;
114 *_logStream << "RINEX File Editing\n";
115 *_logStream << QByteArray(78, '-') << Qt::endl;
116
117 *_logStream << QByteArray("Program").leftJustified(18) << ": "
118 << BNC_CORE->pgmName() << Qt::endl;
119 *_logStream << QByteArray("Run by").leftJustified(18) << ": "
120 << BNC_CORE->userName() << Qt::endl;
121 *_logStream << QByteArray("Date").leftJustified(18) << ": "
122 << QDateTime::currentDateTime().toUTC().toString("yyyy-MM-dd hh:mm:ss") << Qt::endl;
123 *_logStream << QByteArray("RINEX Version").leftJustified(18) << ": "
124 << _rnxVersion << Qt::endl;
125 *_logStream << QByteArray("Sampling").leftJustified(18) << ": "
126 << _samplingRate << " sec" << Qt::endl;
127 *_logStream << QByteArray("Minimum Elevation").leftJustified(18) << ": "
128 << _minEle << " deg" << Qt::endl;
129 *_logStream << QByteArray("Start time").leftJustified(18) << ": "
130 << _begTime.datestr().c_str() << ' '
131 << _begTime.timestr(0).c_str() << Qt::endl;
132 *_logStream << QByteArray("End time").leftJustified(18) << ": "
133 << _endTime.datestr().c_str() << ' '
134 << _endTime.timestr(0).c_str() << Qt::endl;
135 *_logStream << QByteArray("Input Obs Files").leftJustified(18) << ": "
136 << _obsFileNames.join(",") << Qt::endl;
137 *_logStream << QByteArray("Input Nav Files").leftJustified(18) << ": "
138 << _navFileNames.join(",") << Qt::endl;
139 *_logStream << QByteArray("Output Obs File").leftJustified(18) << ": "
140 << _outObsFileName << Qt::endl;
141 *_logStream << QByteArray("Output Nav File").leftJustified(18) << ": "
142 << _outNavFileName << Qt::endl;
143
144 *_logStream << QByteArray(78, '-') << Qt::endl;
145 _logStream->flush();
146 }
147
148 // Handle Observation Files
149 // ------------------------
150 editObservations();
151
152 // Handle Navigations Files
153 // ------------------------
154 editEphemerides();
155
156 // Exit (thread)
157 // -------------
158 if (BNC_CORE->mode() != t_bncCore::interactive) {
159 qApp->exit(9);
160 msleep(100); //sleep 0.1 sec
161 }
162 else {
163 emit finished();
164 deleteLater();
165 }
166
167}
168
169// Initialize input observation files, sort them according to start time
170////////////////////////////////////////////////////////////////////////////
171void t_reqcEdit::initRnxObsFiles(const QStringList& obsFileNames,
172 QVector<t_rnxObsFile*>& rnxObsFiles,
173 QTextStream* log) {
174
175 QStringListIterator it(obsFileNames);
176 while (it.hasNext()) {
177 QString fileName = it.next();
178 if (fileName.indexOf('*') != -1 ||
179 fileName.indexOf('?') != -1) {
180 QFileInfo fileInfo(fileName);
181 QDir dir = fileInfo.dir();
182 QStringList filters; filters << fileInfo.fileName();
183 QListIterator<QFileInfo> it(dir.entryInfoList(filters));
184 while (it.hasNext()) {
185 QString filePath = it.next().filePath();
186 t_rnxObsFile* rnxObsFile = 0;
187 try {
188 rnxObsFile = new t_rnxObsFile(filePath, t_rnxObsFile::input);
189 rnxObsFiles.append(rnxObsFile);
190 }
191 catch (...) {
192 delete rnxObsFile;
193 if (log) {
194 *log << "Error in rnxObsFile " << filePath.toLatin1().data() << Qt::endl;
195 }
196 }
197 }
198 }
199 else {
200 t_rnxObsFile* rnxObsFile = 0;
201 try {
202 rnxObsFile = new t_rnxObsFile(fileName, t_rnxObsFile::input);
203 rnxObsFiles.append(rnxObsFile);
204 }
205 catch (...) {
206 if (log) {
207 *log << "Error in rnxObsFile " << fileName.toLatin1().data() << Qt::endl;
208 }
209 }
210 }
211 }
212 std::stable_sort(rnxObsFiles.begin(), rnxObsFiles.end(),
213 t_rnxObsFile::earlierStartTime);
214}
215
216//
217////////////////////////////////////////////////////////////////////////////
218void t_reqcEdit::editObservations() {
219
220 // Easy Exit
221 // ---------
222 if (_obsFileNames.isEmpty() || _outObsFileName.isEmpty()) {
223 return;
224 }
225
226 t_reqcEdit::initRnxObsFiles(_obsFileNames, _rnxObsFiles, _logStream);
227
228 // Read Ephemerides (needed to apply the elevation mask)
229 // ------------------------------------------------------
230 if (_minEle > 0.0 && _ephs.isEmpty()) {
231 t_reqcEdit::readEphemerides(_navFileNames, _ephs, _logStream, _checkEph);
232 t_reqcEdit::indexEphemerides(_ephs, _ephsByPrn);
233 }
234
235 // Initialize output observation file
236 // ----------------------------------
237 t_rnxObsFile outObsFile(_outObsFileName, t_rnxObsFile::output);
238
239
240 // Put together all run by date entries
241 // ------------------------------------
242 QStringList runByDate;
243 if (_rnxVersion >= 4.0 && _rnxObsFiles.size() > 1) {
244 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
245 t_rnxObsFile* rnxObsFile = _rnxObsFiles[ii];
246 QStringListIterator itRunByDt(rnxObsFile->runByDate());
247 while (itRunByDt.hasNext()) {
248 runByDate.append(itRunByDt.next());
249 }
250 }
251 runByDate.removeDuplicates();
252 }
253
254 // Select observation types
255 // ------------------------
256 bncSettings settings;
257 QStringList useObsTypes = settings.value("reqcUseObsTypes").toString().split(" ", Qt::SkipEmptyParts);
258
259 // Put together all observation types
260 // ----------------------------------
261 if (_rnxObsFiles.size() > 1 && useObsTypes.size() == 0) {
262 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
263 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
264 for (int iSys = 0; iSys < obsFile->numSys(); iSys++) {
265 char sys = obsFile->system(iSys);
266 if (sys != ' ') {
267 for (int iType = 0; iType < obsFile->nTypes(sys); iType++) {
268 QString type = obsFile->obsType(sys, iType);
269 if (_rnxVersion < 3.0) {
270 useObsTypes << type;
271 }
272 else {
273 useObsTypes << QString(sys) + ":" + type;
274 }
275 }
276 }
277 }
278 }
279 useObsTypes.removeDuplicates();
280 }
281
282 // Put together all phase shifts
283 // -----------------------------
284 QStringList phaseShifts;
285 if (_rnxVersion >= 3.0 && _rnxObsFiles.size() > 1) {
286 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
287 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
288 phaseShifts << obsFile->phaseShifts();
289 }
290 phaseShifts.removeDuplicates();
291 }
292
293 // Put together all GLONASS biases
294 // -------------------------------
295 QStringList gloBiases;
296 if (_rnxVersion >= 3.0 && _rnxObsFiles.size() > 1) {
297 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
298 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
299 if (ii == 0 && obsFile->numGloBiases() == 4) {
300 break;
301 }
302 else {
303 gloBiases << obsFile->gloBiases();
304 }
305 }
306 gloBiases.removeDuplicates();
307 }
308
309 // Put together all GLONASS slots
310 // -----------------------------
311 QStringList gloSlots;
312 if (_rnxVersion >= 3.0 && _rnxObsFiles.size() > 1) {
313 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
314 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
315 if (ii == 0 &&
316 obsFile->numGloSlots() == signed(t_prn::MAXPRN_GLONASS)) {
317 break;
318 }
319 else {
320 gloSlots << obsFile->gloSlots();
321 }
322 }
323 gloSlots.removeDuplicates();
324 }
325
326 // Loop over all input observation files
327 // -------------------------------------
328 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
329 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
330 if (_logStream) {
331 *_logStream << "Input Obs File: " << obsFile->fileName() << " start: "
332 << obsFile->startTime().datestr().c_str() << ' '
333 << obsFile->startTime().timestr(0).c_str() << Qt::endl;
334 }
335 if (ii == 0) {
336 outObsFile.setHeader(obsFile->header(), int(_rnxVersion), &useObsTypes,
337 &phaseShifts, &gloBiases, &gloSlots, &runByDate);
338 if (_begTime.valid() && _begTime > outObsFile.startTime()) {
339 outObsFile.setStartTime(_begTime);
340 }
341 if (_samplingRate > outObsFile.interval()) {
342 outObsFile.setInterval(_samplingRate);
343 }
344 editRnxObsHeader(outObsFile);
345 bncSettings settings;
346 QMap<QString, QString> txtMap;
347 QString runBy = settings.value("reqcRunBy").toString();
348 if (!runBy.isEmpty()) {
349 txtMap["RUN BY"] = runBy;
350 }
351 QString comment = settings.value("reqcComment").toString();
352 if (!comment.isEmpty()) {
353 txtMap["COMMENT"] = comment;
354 }
355 if (int(_rnxVersion) < int(obsFile->header().version())) {
356 addRnxConversionDetails(obsFile, txtMap);
357 }
358 outObsFile.header().write(outObsFile.stream(), &txtMap);
359 }
360 t_rnxObsFile::t_rnxEpo* epo = 0;
361 try {
362 while ( (epo = obsFile->nextEpoch()) != 0) {
363 if (_begTime.valid() && epo->tt < _begTime) {
364 continue;
365 }
366 if (_endTime.valid() && epo->tt > _endTime) {
367 break;
368 }
369
370 int sec = int(nint(epo->tt.gpssec()*10));
371 if (sec % (int(_samplingRate)*10) == 0) {
372 applyLLI(obsFile, epo);
373 if (_minEle > 0.0) {
374 applyElevationMask(obsFile, epo);
375 }
376 outObsFile.writeEpoch(epo);
377 }
378 else {
379 rememberLLI(obsFile, epo);
380 }
381 }
382 }
383 catch (QString str) {
384 if (_logStream) {
385 *_logStream << "Exception " << str << Qt::endl;
386 }
387 else {
388 qDebug() << str;
389 }
390 return;
391 }
392 catch (...) {
393 if (_logStream) {
394 *_logStream << "Exception unknown" << Qt::endl;
395 }
396 else {
397 qDebug() << "Exception unknown";
398 }
399 return;
400 }
401 }
402}
403
404// Change RINEX Header Content
405////////////////////////////////////////////////////////////////////////////
406void t_reqcEdit::editRnxObsHeader(t_rnxObsFile& obsFile) {
407
408 bncSettings settings;
409
410 QString oldMarkerName = settings.value("reqcOldMarkerName").toString();
411 QString newMarkerName = settings.value("reqcNewMarkerName").toString();
412 if (!newMarkerName.isEmpty()) {
413 if (oldMarkerName.isEmpty() ||
414 QRegExp(oldMarkerName).exactMatch(obsFile.markerName())) {
415 obsFile.setMarkerName(newMarkerName);
416 }
417 }
418
419 QString oldAntennaName = settings.value("reqcOldAntennaName").toString();
420 QString newAntennaName = settings.value("reqcNewAntennaName").toString();
421 if (!newAntennaName.isEmpty()) {
422 if (oldAntennaName.isEmpty() ||
423 QRegExp(oldAntennaName).exactMatch(obsFile.antennaName())) {
424 obsFile.setAntennaName(newAntennaName);
425 }
426 }
427
428 QString oldAntennaNumber = settings.value("reqcOldAntennaNumber").toString();
429 QString newAntennaNumber = settings.value("reqcNewAntennaNumber").toString();
430 if (!newAntennaNumber.isEmpty()) {
431 if (oldAntennaNumber.isEmpty() ||
432 QRegExp(oldAntennaNumber).exactMatch(obsFile.antennaNumber())) {
433 obsFile.setAntennaNumber(newAntennaNumber);
434 }
435 }
436
437 const ColumnVector& obsFileAntNEU = obsFile.antNEU();
438 QString oldAntennadN = settings.value("reqcOldAntennadN").toString();
439 QString newAntennadN = settings.value("reqcNewAntennadN").toString();
440 if(!newAntennadN.isEmpty()) {
441 if (oldAntennadN.isEmpty() ||
442 oldAntennadN.toDouble() == obsFileAntNEU(1)) {
443 obsFile.setAntennaN(newAntennadN.toDouble());
444 }
445 }
446 QString oldAntennadE = settings.value("reqcOldAntennadE").toString();
447 QString newAntennadE = settings.value("reqcNewAntennadE").toString();
448 if(!newAntennadE.isEmpty()) {
449 if (oldAntennadE.isEmpty() ||
450 oldAntennadE.toDouble() == obsFileAntNEU(2)) {
451 obsFile.setAntennaE(newAntennadE.toDouble());
452 }
453 }
454 QString oldAntennadU = settings.value("reqcOldAntennadU").toString();
455 QString newAntennadU = settings.value("reqcNewAntennadU").toString();
456 if(!newAntennadU.isEmpty()) {
457 if (oldAntennadU.isEmpty() ||
458 oldAntennadU.toDouble() == obsFileAntNEU(3)) {
459 obsFile.setAntennaU(newAntennadU.toDouble());
460 }
461 }
462
463 QString oldReceiverType = settings.value("reqcOldReceiverName").toString();
464 QString newReceiverType = settings.value("reqcNewReceiverName").toString();
465 if (!newReceiverType.isEmpty()) {
466 if (oldReceiverType.isEmpty() ||
467 QRegExp(oldReceiverType).exactMatch(obsFile.receiverType())) {
468 obsFile.setReceiverType(newReceiverType);
469 }
470 }
471
472 QString oldReceiverNumber = settings.value("reqcOldReceiverNumber").toString();
473 QString newReceiverNumber = settings.value("reqcNewReceiverNumber").toString();
474 if (!newReceiverNumber.isEmpty()) {
475 if (oldReceiverNumber.isEmpty() ||
476 QRegExp(oldReceiverNumber).exactMatch(obsFile.receiverNumber())) {
477 obsFile.setReceiverNumber(newReceiverNumber);
478 }
479 }
480}
481
482//
483////////////////////////////////////////////////////////////////////////////
484void t_reqcEdit::rememberLLI(const t_rnxObsFile* obsFile,
485 const t_rnxObsFile::t_rnxEpo* epo) {
486
487 if (_samplingRate == 0) {
488 return;
489 }
490
491 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
492 const t_rnxObsFile::t_rnxSat& rnxSat = epo->rnxSat[iSat];
493 char sys = rnxSat.prn.system();
494 QString prn(rnxSat.prn.toString().c_str());
495
496 for (int iType = 0; iType < obsFile->nTypes(sys); iType++) {
497 QString type = obsFile->obsType(sys, iType);
498 if (!_lli[prn].contains(iType)) {
499 _lli[prn][iType] = 0;
500 }
501 if (rnxSat.obs.contains(type) && rnxSat.obs[type].lli & 1) {
502 _lli[prn][iType] |= 1;
503 }
504 }
505 }
506}
507
508//
509////////////////////////////////////////////////////////////////////////////
510void t_reqcEdit::applyLLI(const t_rnxObsFile* obsFile,
511 t_rnxObsFile::t_rnxEpo* epo) {
512
513 if (_samplingRate == 0) {
514 return;
515 }
516
517 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
518 t_rnxObsFile::t_rnxSat& rnxSat = epo->rnxSat[iSat];
519 char sys = rnxSat.prn.system();
520 QString prn(rnxSat.prn.toString().c_str());
521
522 for (int iType = 0; iType < obsFile->nTypes(sys); iType++) {
523 QString type = obsFile->obsType(sys, iType);
524 if (_lli[prn].contains(iType) && _lli[prn][iType] & 1) {
525 if (rnxSat.obs.contains(type)) {
526 rnxSat.obs[type].lli |= 1;
527 }
528 }
529 }
530 }
531
532 _lli.clear();
533}
534
535// Remove satellites below the minimum elevation angle
536////////////////////////////////////////////////////////////////////////////
537void t_reqcEdit::applyElevationMask(const t_rnxObsFile* obsFile,
538 t_rnxObsFile::t_rnxEpo* epo) {
539
540 const ColumnVector& xyzSta = obsFile->xyz();
541 if (xyzSta.size() != 3 || (xyzSta(1) == 0.0 && xyzSta(2) == 0.0 && xyzSta(3) == 0.0)) {
542 return;
543 }
544
545 std::vector<t_rnxObsFile::t_rnxSat> keptSats;
546 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
547 const t_rnxObsFile::t_rnxSat& rnxSat = epo->rnxSat[iSat];
548
549 t_eph* eph = ephForTime(_ephsByPrn.value(rnxSat.prn), rnxSat.prn, epo->tt);
550 if (eph) {
551 ColumnVector xc(6);
552 ColumnVector vv(3);
553 if (eph->getCrd(epo->tt, xc, vv, false) == success) {
554 double rho, eleSat, azSat;
555 topos(xyzSta(1), xyzSta(2), xyzSta(3), xc(1), xc(2), xc(3), rho, eleSat, azSat);
556 if (eleSat * 180.0/M_PI < _minEle) {
557 continue;
558 }
559 }
560 }
561 keptSats.push_back(rnxSat);
562 }
563 epo->rnxSat = keptSats;
564}
565
566// Ephemeris of the given satellite for epoch tt: the one with TOC closest
567// to tt among those valid at tt (see outDatedBcep). If none is valid,
568// return 0 when validOnly is set, otherwise the one with TOC closest to tt.
569// Only ephemerides accepted by t_eph::getCrd are considered (not bad or
570// outdated; unhealthy ones only if allowUnhealthy is set, to be used with
571// getCrd(..., ignoreHealth = true)).
572// Using the first ephemeris of the day for all epochs gives wrong
573// geometry late in the day and, for GLONASS, integrations over many
574// hours (t_ephGlo::position integrates from TOC on every call).
575////////////////////////////////////////////////////////////////////////////
576t_eph* t_reqcEdit::ephForTime(const QVector<t_eph*>& ephs, const t_prn& prn,
577 const bncTime& tt, bool validOnly,
578 bool allowUnhealthy) {
579 t_eph* bestValid = 0;
580 t_eph* bestAny = 0;
581 double bestValidDt = 0.0;
582 double bestAnyDt = 0.0;
583 for (int ie = 0; ie < ephs.size(); ie++) {
584 t_eph* eph = ephs[ie];
585 if (eph->prn() != prn) {
586 continue;
587 }
588 if (eph->checkState() == t_eph::bad ||
589 eph->checkState() == t_eph::outdated ||
590 (eph->checkState() == t_eph::unhealthy && !allowUnhealthy)) {
591 continue;
592 }
593 double dt = fabs(tt - eph->TOC());
594 if (bestAny == 0 || dt < bestAnyDt) {
595 bestAny = eph;
596 bestAnyDt = dt;
597 }
598 if (!outDatedBcep(eph, tt) && (bestValid == 0 || dt < bestValidDt)) {
599 bestValid = eph;
600 bestValidDt = dt;
601 }
602 }
603 if (bestValid || validOnly) {
604 return bestValid;
605 }
606 return bestAny;
607}
608
609// Group ephemerides by satellite (speeds up ephForTime)
610////////////////////////////////////////////////////////////////////////////
611void t_reqcEdit::indexEphemerides(const QVector<t_eph*>& ephs,
612 QMap<t_prn, QVector<t_eph*> >& ephsByPrn) {
613 ephsByPrn.clear();
614 for (int ie = 0; ie < ephs.size(); ie++) {
615 ephsByPrn[ephs[ie]->prn()].append(ephs[ie]);
616 }
617}
618
619/// Read All Ephemerides
620////////////////////////////////////////////////////////////////////////////
621void t_reqcEdit::readEphemerides(const QStringList& navFileNames,
622 QVector<t_eph*>& ephs, QTextStream* log,
623 bool checkEph) {
624
625 QStringListIterator it(navFileNames);
626 while (it.hasNext()) {
627 QString fileName = it.next();
628 if (fileName.indexOf('*') != -1 ||
629 fileName.indexOf('?') != -1) {
630 QFileInfo fileInfo(fileName);
631 QDir dir = fileInfo.dir();
632 QStringList filters; filters << fileInfo.fileName();
633 QListIterator<QFileInfo> it(dir.entryInfoList(filters));
634 while (it.hasNext()) {
635 QString filePath = it.next().filePath();
636 appendEphemerides(filePath, ephs, log, checkEph);
637 }
638 }
639 else {
640 appendEphemerides(fileName, ephs, log, checkEph);
641 }
642 }
643 // TODO: enable user decision
644 std::stable_sort(ephs.begin(), ephs.end(), t_eph::earlierTime);
645 //std::stable_sort(ephs.begin(), ephs.end(), t_eph::prnSort);
646}
647
648//
649////////////////////////////////////////////////////////////////////////////
650void t_reqcEdit::editEphemerides() {
651
652 // Easy Exit
653 // ---------
654 if (_navFileNames.isEmpty() || _outNavFileName.isEmpty()) {
655 return;
656 }
657 // Concatenate all comments and all run by date lines
658 // --------------------------------------------------
659 QStringList comments;
660 QStringList runByDate;
661 bncSettings settings;
662 QString comment = settings.value("reqcComment").toString();
663 if (!comment.isEmpty()) {
664 comments.append(comment);
665 }
666 QStringListIterator it(_navFileNames);
667 while (it.hasNext()) {
668 QString fileName = it.next();
669
670 t_rnxNavFile rnxNavFile(fileName, t_rnxNavFile::input);
671 QStringListIterator itCmnt(rnxNavFile.comments());
672 while (itCmnt.hasNext()) {
673 comments.append(itCmnt.next());
674 }
675 QStringListIterator itRunByDt(rnxNavFile.runByDate());
676 while (itRunByDt.hasNext()) {
677 runByDate.append(itRunByDt.next());
678 }
679 }
680 comments.removeDuplicates();
681 runByDate.removeDuplicates();
682
683 // Read Ephemerides
684 // ----------------
685 t_reqcEdit::readEphemerides(_navFileNames, _ephs, _logStream, _checkEph);
686
687 // Check Satellite Systems
688 // -----------------------
689 bool haveGPS = false;
690 bool haveGlonass = false;
691 QMap<t_eph::e_system, bool> haveGnss;
692 for (int ii = 0; ii < _ephs.size(); ii++) {
693 const t_eph* eph = _ephs[ii];
694 switch (eph->system()) {
695 case t_eph::GPS:
696 haveGPS = true;
697 haveGnss[t_eph::GPS] = true;
698 break;
699 case t_eph::GLONASS:
700 haveGlonass = true;
701 haveGnss[t_eph::GLONASS] = true;
702 break;
703 case t_eph::Galileo:
704 haveGnss[t_eph::Galileo] = true;
705 break;
706 case t_eph::BDS:
707 haveGnss[t_eph::BDS] = true;
708 break;
709 case t_eph::QZSS:
710 haveGnss[t_eph::QZSS] = true;
711 break;
712 case t_eph::NavIC:
713 haveGnss[t_eph::NavIC] = true;
714 break;
715 case t_eph::SBAS:
716 haveGnss[t_eph::SBAS] = true;
717 break;
718 default:
719 haveGnss[t_eph::unknown] = true;
720 }
721 }
722
723 // Initialize output navigation file
724 // ---------------------------------
725 t_rnxNavFile outNavFile(_outNavFileName, t_rnxNavFile::output);
726
727 outNavFile.setGlonass(haveGlonass);
728
729 if (_rnxVersion < 3.0) {
730 if (haveGPS && haveGlonass) {
731 outNavFile.setVersion(defaultRnxNavVersion3);
732 }
733 if (haveGPS && !haveGlonass) {
734 outNavFile.setVersion(defaultRnxNavVersion2);
735 }
736 if (!haveGPS && haveGlonass) {
737 outNavFile.setVersion(defaultRnxNavVersion2);
738 }
739 }
740
741 if (_rnxVersion >= 3.0 && _rnxVersion < 4.0) {
742 outNavFile.setVersion(defaultRnxNavVersion3);
743 }
744
745 if (_rnxVersion >= 4.0) {
746 outNavFile.setVersion(defaultRnxNavVersion4);
747 }
748
749 if (outNavFile.version() > 3.0) {
750 if (haveGnss.size() > 1) {
751 outNavFile.setGnssTypeV3(t_eph::unknown);
752 }
753 else if (haveGnss.size() == 1){
754 outNavFile.setGnssTypeV3(haveGnss.keys().first());
755 }
756 }
757
758 QMap<QString, QString> txtMap;
759 QString runBy = settings.value("reqcRunBy").toString();
760 if (!runBy.isEmpty()) {
761 txtMap["RUN BY"] = runBy;
762 }
763 if (!runByDate.empty()) {
764 txtMap["RUN BY DATE"] = runByDate.join("\\n");
765 }
766 if (!comments.isEmpty()) {
767 txtMap["COMMENT"] = comments.join("\\n");
768 }
769
770 int mergedNavFiles = _navFileNames.size();
771 unsigned year, month, day;
772 int gps_utc = 0;
773 if (_ephs.size()) {
774 _ephs.at(0)->TOC().civil_date(year, month, day);
775 gps_utc = gnumleap(year, month, day);
776 }
777 outNavFile.writeHeader(&txtMap, mergedNavFiles, gps_utc);
778
779 // Loop over all ephemerides
780 // -------------------------
781 for (int ii = 0; ii < _ephs.size(); ii++) {
782 const t_eph* eph = _ephs[ii];
783 bncTime begTime = _begTime;
784 bncTime endTime = _endTime;
785 if (eph->system() == t_eph::BDS) {
786 begTime += 14;
787 endTime += 14;
788 }
789 if (begTime.valid() && eph->TOC() < begTime) {
790 continue;
791 }
792 if (endTime.valid() && eph->TOC() > endTime) {
793 break;
794 }
795 if (eph->checkState() == t_eph::bad) {
796 continue;
797 }
798
799 if (outNavFile.version() < 3.0) {
800 if (outNavFile.glonass() && eph->system() != t_eph::GLONASS) {
801 continue;
802 }
803 if (!outNavFile.glonass() && eph->system() != t_eph::GPS) {
804 continue;
805 }
806 }
807 outNavFile.writeEph(eph);
808 }
809}
810
811//
812////////////////////////////////////////////////////////////////////////////
813void t_reqcEdit::appendEphemerides(const QString& fileName,
814 QVector<t_eph*>& ephs, QTextStream* log,
815 bool checkEph) {
816 t_rnxNavFile rnxNavFile(fileName, t_rnxNavFile::input);
817 unsigned numOK = 0;
818 unsigned numBad = 0;
819 unsigned numUnhealthy = 0;
820 bncEphUser ephUser(false);
821
822 if (log) {
823 QFileInfo navFi(rnxNavFile.fileName());
824 *log << "Input Nav File : " << navFi.fileName() << Qt::endl;
825 if (checkEph) {
826 *log << "RINEX Version : " << rnxNavFile.version() << Qt::endl;
827 }
828 }
829
830 for (unsigned ii = 0; ii < rnxNavFile.ephs().size(); ii++) {
831 t_eph* eph = rnxNavFile.ephs()[ii];
832 bool isNew = true;
833 for (int iOld = 0; iOld < ephs.size(); iOld++) {
834 const t_eph* ephOld = ephs[iOld];
835 if (ephOld->prn() == eph->prn() &&
836 ephOld->TOC() == eph->TOC()) {
837 isNew = false;
838 break;
839 }
840 }
841
842 if (isNew) {
843
844 if (checkEph) {
845 ephUser.putNewEph(eph, false);
846 if (eph->checkState() == t_eph::bad) {
847 ++numBad;
848 }
849 else if (eph->checkState() == t_eph::unhealthy) {
850 ++numUnhealthy;
851 }
852 else {
853 ++numOK;
854 }
855 if (eph->checkState() == t_eph::bad) {
856 continue;
857 }
858 }
859
860 if (eph->system() == t_eph::GPS) {
861 ephs.append(new t_ephGPS(*dynamic_cast<t_ephGPS*>(eph)));
862 }
863 else if (eph->system() == t_eph::GLONASS) {
864 ephs.append(new t_ephGlo(*dynamic_cast<t_ephGlo*>(eph)));
865 }
866 else if (eph->system() == t_eph::Galileo) {
867 ephs.append(new t_ephGal(*dynamic_cast<t_ephGal*>(eph)));
868 }
869 else if (eph->system() == t_eph::QZSS) {
870 ephs.append(new t_ephGPS(*dynamic_cast<t_ephGPS*>(eph)));
871 }
872 else if (eph->system() == t_eph::SBAS) {
873 ephs.append(new t_ephSBAS(*dynamic_cast<t_ephSBAS*>(eph)));
874 }
875 else if (eph->system() == t_eph::BDS) {
876 ephs.append(new t_ephBDS(*dynamic_cast<t_ephBDS*>(eph)));
877 }
878 else if (eph->system() == t_eph::NavIC) {
879 ephs.append(new t_ephGPS(*dynamic_cast<t_ephGPS*>(eph)));
880 }
881 }
882 }
883
884 if (log && checkEph) {
885 *log << "Ephemeris Check : " << numOK << " OK "
886 << numUnhealthy << " UNHEALTHY "
887 << numBad << " WRONG\n";
888 if (numBad > 0) {
889 for (unsigned ii = 0; ii < rnxNavFile.ephs().size(); ii++) {
890 t_eph* eph = rnxNavFile.ephs()[ii];
891 QFileInfo navFi(fileName);
892 if (eph->checkState() == t_eph::bad) {
893 if (log) {
894 *log << " : "
895 << QString("WRONG %2:%3\n")
896 .arg(eph->typeStr(eph->type(), eph->prn(), 99.0))
897 .arg(eph->rinexDateStr(eph->TOC(), eph->prn(), 99.0)).toLatin1();
898 }
899 }/* switched off because of to many SBAS satellites are set to unhealthy
900 if (eph->checkState() == t_eph::unhealthy) {
901 if (log) {
902 *log << " : "
903 << QString("UNHEALTHY %2:%3\n")
904 .arg(eph->typeStr(eph->type(), eph->prn(), 99.0))
905 .arg(eph->rinexDateStr(eph->TOC(), eph->prn(), 99.0)).toLatin1();
906 }
907 }*/
908 }
909 }
910 *log << Qt::endl;
911 }
912}
913
914void t_reqcEdit::addRnxConversionDetails(const t_rnxObsFile* obsFile,
915 QMap<QString, QString>& txtMap) {
916
917 int key = 0;
918 QString systems = obsFile->header().usedSystems();
919 QString comment = QString("Signal priorities for RINEX 3 => 2 conversion:");
920 QString commentKey = QString("COMMENT %1").arg(key, 3, 10, QChar('0'));
921 txtMap.insert(commentKey, comment);
922
923 for(int ii = 0; ii < obsFile->numSys(); ii++) {
924 char sys = systems[ii].toLatin1();
925 txtMap.insert(commentKey, comment);
926 QMap <char, QString> signalPriorityMap;
927 QStringList preferredAttribListSys = obsFile->signalPriorities(sys);
928 QStringList types = obsFile->header().obsTypes(sys);
929 for (int jj = 0; jj < types.size(); jj++) {
930 QString inType = types[jj];
931 char band = inType[1].toLatin1();
932 for (int ii = 0; ii < preferredAttribListSys.size(); ii++) {
933 QString preferredAttrib;
934 if (preferredAttribListSys[ii].indexOf("&") != -1) {
935 QStringList hlp = preferredAttribListSys[ii].split("&", Qt::SkipEmptyParts);
936 if (hlp.size() == 2 && hlp[0].contains(band)) {
937 preferredAttrib = hlp[1];
938 }
939 }
940 else {
941 preferredAttrib = preferredAttribListSys[ii];
942 }
943 if (!signalPriorityMap.contains(band) && !preferredAttrib.isEmpty()){
944 signalPriorityMap[band] = preferredAttrib;
945 }
946 }
947 }
948 QMapIterator<char, QString> it(signalPriorityMap);
949 while (it.hasNext()) {
950 it.next();
951 key++;
952 comment = QString("%1 band %2: %3").arg(sys).arg(it.key()).arg(it.value());
953 commentKey = QString("COMMENT %1").arg(key, 3, 10, QChar('0'));
954 txtMap.insert(commentKey, comment);
955 }
956 }
957}
Note: See TracBrowser for help on using the repository browser.