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

Last change on this file since 6874 was 6841, checked in by stuerze, 10 years ago

consideration of obs header etries for phase shifts, GLONASS slots and GLONASS biases during merging of RINEX files

File size: 19.5 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://www.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 _log = 0;
60 _obsFileNames = settings.value("reqcObsFile").toString().split(",", QString::SkipEmptyParts);
61 _outObsFileName = settings.value("reqcOutObsFile").toString();
62 _navFileNames = settings.value("reqcNavFile").toString().split(",", QString::SkipEmptyParts);
63 _outNavFileName = settings.value("reqcOutNavFile").toString();
64 int version = settings.value("reqcRnxVersion").toInt();
65 if (version < 3) {
66 _rnxVersion = t_rnxObsHeader::defaultRnxObsVersion2;
67 }
68 else {
69 _rnxVersion = t_rnxObsHeader::defaultRnxObsVersion3;
70 }
71 _samplingRate = settings.value("reqcSampling").toInt();
72 _begTime = bncTime(settings.value("reqcStartDateTime").toString().toAscii().data());
73 _endTime = bncTime(settings.value("reqcEndDateTime").toString().toAscii().data());
74}
75
76// Destructor
77////////////////////////////////////////////////////////////////////////////
78t_reqcEdit::~t_reqcEdit() {
79 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
80 delete _rnxObsFiles[ii];
81 }
82 for (int ii = 0; ii < _ephs.size(); ii++) {
83 delete _ephs[ii];
84 }
85 delete _log; _log = 0;
86 delete _logFile; _logFile = 0;
87}
88
89//
90////////////////////////////////////////////////////////////////////////////
91void t_reqcEdit::run() {
92
93 // Open Log File
94 // -------------
95 _logFile = new QFile(_logFileName);
96 if (_logFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
97 _log = new QTextStream();
98 _log->setDevice(_logFile);
99 }
100
101 // Log File Header
102 // ---------------
103 if (_log) {
104 *_log << QByteArray(78, '-') << endl;
105 *_log << "Concatenation of RINEX Observation and/or Navigation Files\n";
106 *_log << QByteArray(78, '-') << endl;
107
108 *_log << QByteArray("Program").leftJustified(15) << ": "
109 << BNC_CORE->pgmName() << endl;
110 *_log << QByteArray("Run by").leftJustified(15) << ": "
111 << BNC_CORE->userName() << endl;
112 *_log << QByteArray("Date").leftJustified(15) << ": "
113 << QDateTime::currentDateTime().toUTC().toString("yyyy-MM-dd hh:mm:ss") << endl;
114 *_log << QByteArray("RINEX Version").leftJustified(15) << ": "
115 << _rnxVersion << endl;
116 *_log << QByteArray("Sampling").leftJustified(15) << ": "
117 << _samplingRate << endl;
118 *_log << QByteArray("Start time").leftJustified(15) << ": "
119 << _begTime.datestr().c_str() << ' '
120 << _begTime.timestr(0).c_str() << endl;
121 *_log << QByteArray("End time").leftJustified(15) << ": "
122 << _endTime.datestr().c_str() << ' '
123 << _endTime.timestr(0).c_str() << endl;
124 *_log << QByteArray("Input Obs Files").leftJustified(15) << ": "
125 << _obsFileNames.join(",") << endl;
126 *_log << QByteArray("Input Nav Files").leftJustified(15) << ": "
127 << _navFileNames.join(",") << endl;
128 *_log << QByteArray("Output Obs File").leftJustified(15) << ": "
129 << _outObsFileName << endl;
130 *_log << QByteArray("Output Nav File").leftJustified(15) << ": "
131 << _outNavFileName << endl;
132
133 *_log << QByteArray(78, '-') << endl;
134 _log->flush();
135 }
136
137 // Handle Observation Files
138 // ------------------------
139 editObservations();
140
141 // Handle Navigations Files
142 // ------------------------
143 editEphemerides();
144
145 // Exit (thread)
146 // -------------
147 if (BNC_CORE->mode() != t_bncCore::interactive) {
148 qApp->exit(0);
149 }
150 else {
151 emit finished();
152 deleteLater();
153 }
154}
155
156// Initialize input observation files, sort them according to start time
157////////////////////////////////////////////////////////////////////////////
158void t_reqcEdit::initRnxObsFiles(const QStringList& obsFileNames,
159 QVector<t_rnxObsFile*>& rnxObsFiles,
160 QTextStream* log) {
161
162 QStringListIterator it(obsFileNames);
163 while (it.hasNext()) {
164 QString fileName = it.next();
165 if (fileName.indexOf('*') != -1 || fileName.indexOf('?') != -1) {
166 QFileInfo fileInfo(fileName);
167 QDir dir = fileInfo.dir();
168 QStringList filters; filters << fileInfo.fileName();
169 QListIterator<QFileInfo> it(dir.entryInfoList(filters));
170 while (it.hasNext()) {
171 QString filePath = it.next().filePath();
172 t_rnxObsFile* rnxObsFile = 0;
173 try {
174 rnxObsFile = new t_rnxObsFile(filePath, t_rnxObsFile::input);
175 rnxObsFiles.append(rnxObsFile);
176 }
177 catch (...) {
178 delete rnxObsFile;
179 if (log) {
180 *log << "Error in rnxObsFile " << filePath.toAscii().data() << endl;
181 }
182 }
183 }
184 }
185 else {
186 t_rnxObsFile* rnxObsFile = 0;
187 try {
188 rnxObsFile = new t_rnxObsFile(fileName, t_rnxObsFile::input);
189 rnxObsFiles.append(rnxObsFile);
190 }
191 catch (...) {
192 if (log) {
193 *log << "Error in rnxObsFile " << fileName.toAscii().data() << endl;
194 }
195 }
196 }
197 }
198 qStableSort(rnxObsFiles.begin(), rnxObsFiles.end(),
199 t_rnxObsFile::earlierStartTime);
200}
201
202//
203////////////////////////////////////////////////////////////////////////////
204void t_reqcEdit::editObservations() {
205
206 // Easy Exit
207 // ---------
208 if (_obsFileNames.isEmpty() || _outObsFileName.isEmpty()) {
209 return;
210 }
211
212 t_reqcEdit::initRnxObsFiles(_obsFileNames, _rnxObsFiles, _log);
213
214 // Initialize output observation file
215 // ----------------------------------
216 t_rnxObsFile outObsFile(_outObsFileName, t_rnxObsFile::output);
217
218 // Select observation types
219 // ------------------------
220 bncSettings settings;
221 QStringList useObsTypes = settings.value("reqcUseObsTypes").toString().split(" ", QString::SkipEmptyParts);
222
223 // Put together all observation types
224 // ----------------------------------
225 if (_rnxObsFiles.size() > 1 && useObsTypes.size() == 0) {
226 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
227 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
228 for (int iSys = 0; iSys < obsFile->numSys(); iSys++) {
229 char sys = obsFile->system(iSys);
230 if (sys != ' ') {
231 for (int iType = 0; iType < obsFile->nTypes(sys); iType++) {
232 QString type = obsFile->obsType(sys, iType);
233 if (_rnxVersion < 3.0) {
234 useObsTypes << type;
235 }
236 else {
237 useObsTypes << QString(sys) + ":" + type;
238 }
239 }
240 }
241 }
242 }
243 useObsTypes.removeDuplicates();
244 }
245
246 // Put together all phase shifts
247 // -----------------------------
248 QStringList phaseShifts;
249 if (_rnxVersion >= 3.0 && _rnxObsFiles.size() > 1) {
250 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
251 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
252 phaseShifts << obsFile->phaseShifts();
253 }
254 phaseShifts.removeDuplicates();
255 }
256
257 // Put together all GLONASS biases
258 // -------------------------------
259 QStringList gloBiases;
260 if (_rnxVersion >= 3.0 && _rnxObsFiles.size() > 1) {
261 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
262 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
263 if (ii == 0 && obsFile->numGloBiases() == 4) {
264 break;
265 }
266 else {
267 gloBiases << obsFile->gloBiases();
268 }
269 }
270 gloBiases.removeDuplicates();
271 }
272
273 // Put together all GLONASS slots
274 // -----------------------------
275 QStringList gloSlots;
276 if (_rnxVersion >= 3.0 && _rnxObsFiles.size() > 1) {
277 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
278 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
279 if (ii == 0 &&
280 obsFile->numGloSlots() == signed(t_prn::MAXPRN_GLONASS)) {
281 break;
282 }
283 else {
284 gloSlots << obsFile->gloSlots();
285 }
286 }
287 gloSlots.removeDuplicates();
288 }
289
290 // Loop over all input observation files
291 // -------------------------------------
292 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) {
293 t_rnxObsFile* obsFile = _rnxObsFiles[ii];
294 if (_log) {
295 *_log << "Processing File: " << obsFile->fileName() << " start: "
296 << obsFile->startTime().datestr().c_str() << ' '
297 << obsFile->startTime().timestr(0).c_str() << endl;
298 }
299 if (ii == 0) {
300 outObsFile.setHeader(obsFile->header(), int(_rnxVersion), &useObsTypes,
301 &phaseShifts, &gloBiases, &gloSlots);
302 if (_begTime.valid() && _begTime > outObsFile.startTime()) {
303 outObsFile.setStartTime(_begTime);
304 }
305 if (_samplingRate > outObsFile.interval()) {
306 outObsFile.setInterval(_samplingRate);
307 }
308 editRnxObsHeader(outObsFile);
309 bncSettings settings;
310 QMap<QString, QString> txtMap;
311 QString runBy = settings.value("reqcRunBy").toString();
312 if (!runBy.isEmpty()) {
313 txtMap["RUN BY"] = runBy;
314 }
315 QString comment = settings.value("reqcComment").toString();
316 if (!comment.isEmpty()) {
317 txtMap["COMMENT"] = comment;
318 }
319 outObsFile.header().write(outObsFile.stream(), &txtMap);
320 }
321 t_rnxObsFile::t_rnxEpo* epo = 0;
322 try {
323 while ( (epo = obsFile->nextEpoch()) != 0) {
324 if (_begTime.valid() && epo->tt < _begTime) {
325 continue;
326 }
327 if (_endTime.valid() && epo->tt > _endTime) {
328 break;
329 }
330
331 if (_samplingRate == 0 ||
332 fmod(round(epo->tt.gpssec()), _samplingRate) == 0) {
333 applyLLI(obsFile, epo);
334 outObsFile.writeEpoch(epo);
335 }
336 else {
337 rememberLLI(obsFile, epo);
338 }
339 }
340 }
341 catch (QString str) {
342 if (_log) {
343 *_log << "Exception " << str << endl;
344 }
345 else {
346 qDebug() << str;
347 }
348 return;
349 }
350 catch (...) {
351 if (_log) {
352 *_log << "Exception unknown" << endl;
353 }
354 else {
355 qDebug() << "Exception unknown";
356 }
357 return;
358 }
359 }
360}
361
362// Change RINEX Header Content
363////////////////////////////////////////////////////////////////////////////
364void t_reqcEdit::editRnxObsHeader(t_rnxObsFile& obsFile) {
365
366 bncSettings settings;
367
368 QString oldMarkerName = settings.value("reqcOldMarkerName").toString();
369 QString newMarkerName = settings.value("reqcNewMarkerName").toString();
370 if (!newMarkerName.isEmpty()) {
371 if (oldMarkerName.isEmpty() ||
372 QRegExp(oldMarkerName).exactMatch(obsFile.markerName())) {
373 obsFile.setMarkerName(newMarkerName);
374 }
375 }
376
377 QString oldAntennaName = settings.value("reqcOldAntennaName").toString();
378 QString newAntennaName = settings.value("reqcNewAntennaName").toString();
379 if (!newAntennaName.isEmpty()) {
380 if (oldAntennaName.isEmpty() ||
381 QRegExp(oldAntennaName).exactMatch(obsFile.antennaName())) {
382 obsFile.setAntennaName(newAntennaName);
383 }
384 }
385
386 QString oldAntennaNumber = settings.value("reqcOldAntennaNumber").toString();
387 QString newAntennaNumber = settings.value("reqcNewAntennaNumber").toString();
388 if (!newAntennaNumber.isEmpty()) {
389 if (oldAntennaNumber.isEmpty() ||
390 QRegExp(oldAntennaNumber).exactMatch(obsFile.antennaNumber())) {
391 obsFile.setAntennaNumber(newAntennaNumber);
392 }
393 }
394
395
396 const ColumnVector& obsFileAntNEU = obsFile.antNEU();
397 QString oldAntennadN = settings.value("reqcOldAntennadN").toString();
398 QString newAntennadN = settings.value("reqcNewAntennadN").toString();
399 if(!newAntennadN.isEmpty()) {
400 if (oldAntennadN.isEmpty() ||
401 oldAntennadN.toDouble() == obsFileAntNEU(1)) {
402 obsFile.setAntennaN(newAntennadN.toDouble());
403 }
404 }
405 QString oldAntennadE = settings.value("reqcOldAntennadE").toString();
406 QString newAntennadE = settings.value("reqcNewAntennadE").toString();
407 if(!newAntennadE.isEmpty()) {
408 if (oldAntennadE.isEmpty() ||
409 oldAntennadE.toDouble() == obsFileAntNEU(2)) {
410 obsFile.setAntennaE(newAntennadE.toDouble());
411 }
412 }
413 QString oldAntennadU = settings.value("reqcOldAntennadU").toString();
414 QString newAntennadU = settings.value("reqcNewAntennadU").toString();
415 if(!newAntennadU.isEmpty()) {
416 if (oldAntennadU.isEmpty() ||
417 oldAntennadU.toDouble() == obsFileAntNEU(3)) {
418 obsFile.setAntennaU(newAntennadU.toDouble());
419 }
420 }
421
422 QString oldReceiverType = settings.value("reqcOldReceiverName").toString();
423 QString newReceiverType = settings.value("reqcNewReceiverName").toString();
424 if (!newReceiverType.isEmpty()) {
425 if (oldReceiverType.isEmpty() ||
426 QRegExp(oldReceiverType).exactMatch(obsFile.receiverType())) {
427 obsFile.setReceiverType(newReceiverType);
428 }
429 }
430
431 QString oldReceiverNumber = settings.value("reqcOldReceiverNumber").toString();
432 QString newReceiverNumber = settings.value("reqcNewReceiverNumber").toString();
433 if (!newReceiverNumber.isEmpty()) {
434 if (oldReceiverNumber.isEmpty() ||
435 QRegExp(oldReceiverNumber).exactMatch(obsFile.receiverNumber())) {
436 obsFile.setReceiverNumber(newReceiverNumber);
437 }
438 }
439}
440
441//
442////////////////////////////////////////////////////////////////////////////
443void t_reqcEdit::rememberLLI(const t_rnxObsFile* obsFile,
444 const t_rnxObsFile::t_rnxEpo* epo) {
445
446 if (_samplingRate == 0) {
447 return;
448 }
449
450 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
451 const t_rnxObsFile::t_rnxSat& rnxSat = epo->rnxSat[iSat];
452 char sys = rnxSat.prn.system();
453 QString prn(rnxSat.prn.toString().c_str());
454
455 for (int iType = 0; iType < obsFile->nTypes(sys); iType++) {
456 QString type = obsFile->obsType(sys, iType);
457 if (!_lli[prn].contains(iType)) {
458 _lli[prn][iType] = 0;
459 }
460 if (rnxSat.obs.contains(type) && rnxSat.obs[type].lli & 1) {
461 _lli[prn][iType] |= 1;
462 }
463 }
464 }
465}
466
467//
468////////////////////////////////////////////////////////////////////////////
469void t_reqcEdit::applyLLI(const t_rnxObsFile* obsFile,
470 t_rnxObsFile::t_rnxEpo* epo) {
471
472 if (_samplingRate == 0) {
473 return;
474 }
475
476 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
477 t_rnxObsFile::t_rnxSat& rnxSat = epo->rnxSat[iSat];
478 char sys = rnxSat.prn.system();
479 QString prn(rnxSat.prn.toString().c_str());
480
481 for (int iType = 0; iType < obsFile->nTypes(sys); iType++) {
482 QString type = obsFile->obsType(sys, iType);
483 if (_lli[prn].contains(iType) && _lli[prn][iType] & 1) {
484 if (rnxSat.obs.contains(type)) {
485 rnxSat.obs[type].lli |= 1;
486 }
487 }
488 }
489 }
490
491 _lli.clear();
492}
493
494/// Read All Ephemerides
495////////////////////////////////////////////////////////////////////////////
496void t_reqcEdit::readEphemerides(const QStringList& navFileNames,
497 QVector<t_eph*>& ephs) {
498
499 QStringListIterator it(navFileNames);
500 while (it.hasNext()) {
501 QString fileName = it.next();
502 if (fileName.indexOf('*') != -1 || fileName.indexOf('?') != -1) {
503 QFileInfo fileInfo(fileName);
504 QDir dir = fileInfo.dir();
505 QStringList filters; filters << fileInfo.fileName();
506 QListIterator<QFileInfo> it(dir.entryInfoList(filters));
507 while (it.hasNext()) {
508 QString filePath = it.next().filePath();
509 appendEphemerides(filePath, ephs);
510 }
511 }
512 else {
513 appendEphemerides(fileName, ephs);
514 }
515 }
516 qStableSort(ephs.begin(), ephs.end(), t_eph::earlierTime);
517}
518
519//
520////////////////////////////////////////////////////////////////////////////
521void t_reqcEdit::editEphemerides() {
522
523 // Easy Exit
524 // ---------
525 if (_navFileNames.isEmpty() || _outNavFileName.isEmpty()) {
526 return;
527 }
528
529 // Read Ephemerides
530 // ----------------
531 t_reqcEdit::readEphemerides(_navFileNames, _ephs);
532
533 // Check Satellite Systems
534 // -----------------------
535 bool haveGPS = false;
536 bool haveGlonass = false;
537 for (int ii = 0; ii < _ephs.size(); ii++) {
538 const t_eph* eph = _ephs[ii];
539 if (eph->type() == t_eph::GPS) {
540 haveGPS = true;
541 }
542 else if (eph->type() == t_eph::GLONASS) {
543 haveGlonass = true;
544 }
545 }
546
547 // Initialize output navigation file
548 // ---------------------------------
549 t_rnxNavFile outNavFile(_outNavFileName, t_rnxNavFile::output);
550
551 outNavFile.setGlonass(haveGlonass);
552
553 if ( (haveGPS && haveGlonass) || _rnxVersion >= 3.0) {
554 outNavFile.setVersion(t_rnxNavFile::defaultRnxNavVersion3);
555 }
556 else {
557 outNavFile.setVersion(t_rnxNavFile::defaultRnxNavVersion2);
558 }
559
560 bncSettings settings;
561 QMap<QString, QString> txtMap;
562 QString runBy = settings.value("reqcRunBy").toString();
563 if (!runBy.isEmpty()) {
564 txtMap["RUN BY"] = runBy;
565 }
566 QString comment = settings.value("reqcComment").toString();
567 if (!comment.isEmpty()) {
568 txtMap["COMMENT"] = comment;
569 }
570
571 outNavFile.writeHeader(&txtMap);
572
573 // Loop over all ephemerides
574 // -------------------------
575 for (int ii = 0; ii < _ephs.size(); ii++) {
576 const t_eph* eph = _ephs[ii];
577 outNavFile.writeEph(eph);
578 }
579}
580
581//
582////////////////////////////////////////////////////////////////////////////
583void t_reqcEdit::appendEphemerides(const QString& fileName,
584 QVector<t_eph*>& ephs) {
585
586 t_rnxNavFile rnxNavFile(fileName, t_rnxNavFile::input);
587 for (unsigned ii = 0; ii < rnxNavFile.ephs().size(); ii++) {
588 t_eph* eph = rnxNavFile.ephs()[ii];
589 bool isNew = true;
590 for (int iOld = 0; iOld < ephs.size(); iOld++) {
591 const t_eph* ephOld = ephs[iOld];
592 if (ephOld->prn() == eph->prn() &&
593 ephOld->TOC() == eph->TOC()) {
594 isNew = false;
595 break;
596 }
597 }
598 if (isNew) {
599 if (eph->type() == t_eph::GPS) {
600 ephs.append(new t_ephGPS(*dynamic_cast<t_ephGPS*>(eph)));
601 }
602 else if (eph->type() == t_eph::GLONASS) {
603 ephs.append(new t_ephGlo(*dynamic_cast<t_ephGlo*>(eph)));
604 }
605 else if (eph->type() == t_eph::Galileo) {
606 ephs.append(new t_ephGal(*dynamic_cast<t_ephGal*>(eph)));
607 }
608 else if (eph->type() == t_eph::QZSS) {
609 ephs.append(new t_ephGPS(*dynamic_cast<t_ephGPS*>(eph)));
610 }
611 else if (eph->type() == t_eph::SBAS) {
612 ephs.append(new t_ephSBAS(*dynamic_cast<t_ephSBAS*>(eph)));
613 }
614 else if (eph->type() == t_eph::BDS) {
615 ephs.append(new t_ephBDS(*dynamic_cast<t_ephBDS*>(eph)));
616 }
617 }
618 }
619}
Note: See TracBrowser for help on using the repository browser.