source: ntrip/trunk/BNC/rinex/rnxobsfile.cpp@ 3864

Last change on this file since 3864 was 3864, checked in by mervart, 12 years ago
File size: 19.1 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_rnxObsFile
30 *
31 * Purpose: Reads RINEX Observation File
32 *
33 * Author: L. Mervart
34 *
35 * Created: 24-Jan-2012
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <iostream>
42#include <iomanip>
43#include <sstream>
44#include "rnxobsfile.h"
45#include "bncutils.h"
46#include "bncversion.h"
47
48using namespace std;
49
50const QString t_rnxObsFile::t_rnxObsHeader::_emptyStr;
51
52// Constructor
53////////////////////////////////////////////////////////////////////////////
54t_rnxObsFile::t_rnxObsHeader::t_rnxObsHeader() {
55 _antNEU.ReSize(3); _antNEU = 0.0;
56 _antXYZ.ReSize(3); _antXYZ = 0.0;
57 _antBSG.ReSize(3); _antBSG = 0.0;
58 _xyz.ReSize(3); _xyz = 0.0;
59 _version = 0.0;
60 _interval = 0.0;
61 for (unsigned iPrn = 1; iPrn <= MAXPRN_GPS; iPrn++) {
62 _wlFactorsL1[iPrn] = 1;
63 _wlFactorsL2[iPrn] = 1;
64 }
65}
66
67// Destructor
68////////////////////////////////////////////////////////////////////////////
69t_rnxObsFile::t_rnxObsHeader::~t_rnxObsHeader() {
70}
71
72// Read Header
73////////////////////////////////////////////////////////////////////////////
74t_irc t_rnxObsFile::t_rnxObsHeader::read(QTextStream* stream, int maxLines) {
75 int numLines = 0;
76 while ( stream->status() == QTextStream::Ok && !stream->atEnd() ) {
77 QString line = stream->readLine(); ++ numLines;
78 if (line.isEmpty()) {
79 continue;
80 }
81 if (line.indexOf("END OF FILE") != -1) {
82 break;
83 }
84 QString value = line.mid(0,60).trimmed();
85 QString key = line.mid(60).trimmed();
86 if (key == "END OF HEADER") {
87 break;
88 }
89 else if (key == "RINEX VERSION / TYPE") {
90 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
91 in >> _version;
92 }
93 else if (key == "MARKER NAME") {
94 _markerName = value;
95 }
96 else if (key == "ANT # / TYPE") {
97 _antennaName = line.mid(20,20).trimmed();
98 }
99 else if (key == "INTERVAL") {
100 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
101 in >> _interval;
102 }
103 else if (key == "WAVELENGTH FACT L1/2") {
104 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
105 int wlFactL1 = 0;
106 int wlFactL2 = 0;
107 int numSat = 0;
108 in >> wlFactL1 >> wlFactL2 >> numSat;
109 if (numSat == 0) {
110 for (unsigned iPrn = 1; iPrn <= MAXPRN_GPS; iPrn++) {
111 _wlFactorsL1[iPrn] = wlFactL1;
112 _wlFactorsL2[iPrn] = wlFactL2;
113 }
114 }
115 else {
116 for (int ii = 0; ii < numSat; ii++) {
117 QString prn; in >> prn;
118 if (prn[0] == 'G') {
119 int iPrn;
120 readInt(prn, 1, 2, iPrn);
121 _wlFactorsL1[iPrn] = wlFactL1;
122 _wlFactorsL2[iPrn] = wlFactL2;
123 }
124 }
125 }
126 }
127 else if (key == "APPROX POSITION XYZ") {
128 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
129 in >> _xyz[0] >> _xyz[1] >> _xyz[2];
130 }
131 else if (key == "ANTENNA: DELTA H/E/N") {
132 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
133 in >> _antNEU[2] >> _antNEU[1] >> _antNEU[0];
134 }
135 else if (key == "ANTENNA: DELTA X/Y/Z") {
136 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
137 in >> _antXYZ[0] >> _antXYZ[1] >> _antXYZ[2];
138 }
139 else if (key == "ANTENNA: B.SIGHT XYZ") {
140 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
141 in >> _antBSG[0] >> _antBSG[1] >> _antBSG[2];
142 }
143 else if (key == "# / TYPES OF OBSERV") {
144 QTextStream* in = new QTextStream(value.toAscii(), QIODevice::ReadOnly);
145 int nTypes;
146 *in >> nTypes;
147 _obsTypesV2.clear();
148 for (int ii = 0; ii < nTypes; ii++) {
149 if (ii > 0 && ii % 9 == 0) {
150 line = stream->readLine(); ++numLines;
151 delete in;
152 in = new QTextStream(line.toAscii(), QIODevice::ReadOnly);
153 }
154 QString hlp;
155 *in >> hlp;
156 _obsTypesV2.push_back(hlp);
157 }
158 }
159 else if (key == "SYS / # / OBS TYPES") {
160 QTextStream* in = new QTextStream(value.toAscii(), QIODevice::ReadOnly);
161 char sys;
162 int nTypes;
163 *in >> sys >> nTypes;
164 _obsTypesV3[sys].clear();
165 for (int ii = 0; ii < nTypes; ii++) {
166 if (ii > 0 && ii % 13 == 0) {
167 line = stream->readLine(); ++numLines;
168 delete in;
169 in = new QTextStream(line.toAscii(), QIODevice::ReadOnly);
170 }
171 QString hlp;
172 *in >> hlp;
173 _obsTypesV3[sys].push_back(hlp);
174 }
175 delete in;
176 }
177 else if (key == "TIME OF FIRST OBS") {
178 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
179 int year, month, day, hour, min;
180 double sec;
181 in >> year >> month >> day >> hour >> min >> sec;
182 _startTime.set(year, month, day, hour, min, sec);
183 }
184 if (maxLines > 0 && numLines == maxLines) {
185 break;
186 }
187 }
188
189 return success;
190}
191
192// Number of Observation Types (satellite-system specific)
193////////////////////////////////////////////////////////////////////////////
194int t_rnxObsFile::t_rnxObsHeader::nTypes(char sys) const {
195 if (_version < 3.0) {
196 return _obsTypesV2.size();
197 }
198 else {
199 map<char, vector<QString> >::const_iterator it = _obsTypesV3.find(sys);
200 if (it != _obsTypesV3.end()) {
201 return it->second.size();
202 }
203 else {
204 return 0;
205 }
206 }
207}
208
209// Observation Type (satellite-system specific)
210////////////////////////////////////////////////////////////////////////////
211const QString& t_rnxObsFile::t_rnxObsHeader::obsType(char sys, int index) const {
212 if (_version < 3.0) {
213 return _obsTypesV2.at(index);
214 }
215 else {
216 map<char, vector<QString> >::const_iterator it = _obsTypesV3.find(sys);
217 if (it != _obsTypesV3.end()) {
218 return it->second.at(index);
219 }
220 else {
221 return _emptyStr;
222 }
223 }
224}
225
226// Constructor
227////////////////////////////////////////////////////////////////////////////
228t_rnxObsFile::t_rnxObsFile(const QString& fileName, e_inpOut inpOut) {
229 _inpOut = inpOut;
230 _stream = 0;
231 _flgPowerFail = false;
232 if (_inpOut == input) {
233 openRead(fileName);
234 }
235 else {
236 openWrite(fileName);
237 }
238}
239
240// Open for input
241////////////////////////////////////////////////////////////////////////////
242void t_rnxObsFile::openRead(const QString& fileName) {
243
244 _fileName = fileName; expandEnvVar(_fileName);
245 _file = new QFile(_fileName);
246 _file->open(QIODevice::ReadOnly | QIODevice::Text);
247 _stream = new QTextStream();
248 _stream->setDevice(_file);
249
250 _header.read(_stream);
251
252 // Guess Observation Interval
253 // --------------------------
254 if (_header._interval == 0.0) {
255 bncTime ttPrev;
256 for (int iEpo = 0; iEpo < 10; iEpo++) {
257 const t_rnxEpo* rnxEpo = nextEpoch();
258 if (!rnxEpo) {
259 throw QString("t_rnxObsFile: not enough epochs");
260 }
261 if (iEpo > 0) {
262 double dt = rnxEpo->tt - ttPrev;
263 if (_header._interval == 0.0 || dt < _header._interval) {
264 _header._interval = dt;
265 }
266 }
267 ttPrev = rnxEpo->tt;
268 }
269 _stream->seek(0);
270 _header.read(_stream);
271 }
272
273 // Time of first observation
274 // -------------------------
275 if (!_header._startTime.valid()) {
276 const t_rnxEpo* rnxEpo = nextEpoch();
277 if (!rnxEpo) {
278 throw QString("t_rnxObsFile: not enough epochs");
279 }
280 _header._startTime = rnxEpo->tt;
281 _stream->seek(0);
282 _header.read(_stream);
283 }
284}
285
286// Open for output
287////////////////////////////////////////////////////////////////////////////
288void t_rnxObsFile::openWrite(const QString& fileName) {
289
290 _fileName = fileName; expandEnvVar(_fileName);
291 _file = new QFile(_fileName);
292 _file->open(QIODevice::WriteOnly | QIODevice::Text);
293 _stream = new QTextStream();
294 _stream->setDevice(_file);
295}
296
297// Destructor
298////////////////////////////////////////////////////////////////////////////
299t_rnxObsFile::~t_rnxObsFile() {
300 close();
301}
302
303// Close
304////////////////////////////////////////////////////////////////////////////
305void t_rnxObsFile::close() {
306 delete _stream; _stream = 0;
307 delete _file; _file = 0;
308}
309
310// Handle Special Epoch Flag
311////////////////////////////////////////////////////////////////////////////
312void t_rnxObsFile::handleEpochFlag(int flag, const QString& line) {
313
314 // Power Failure
315 // -------------
316 if (flag == 1) {
317 _flgPowerFail = true;
318 }
319
320 // Start moving antenna
321 // --------------------
322 else if (flag == 2) {
323 // no action
324 }
325
326 // Re-Read Header
327 // --------------
328 else if (flag == 3 || flag == 4) {
329 int numLines = 0;
330 if (version() < 3.0) {
331 readInt(line, 29, 3, numLines);
332 }
333 else {
334 readInt(line, 32, 3, numLines);
335 }
336 _header.read(_stream, numLines);
337 }
338
339 // Unhandled Flag
340 // --------------
341 else {
342 throw QString("t_rnxObsFile: unhandled flag\n" + line);
343 }
344}
345
346// Retrieve single Epoch
347////////////////////////////////////////////////////////////////////////////
348const t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpoch() {
349
350 _currEpo.clear();
351
352 if (version() < 3.0) {
353 return nextEpochV2();
354 }
355 else {
356 return nextEpochV3();
357 }
358}
359
360// Retrieve single Epoch (RINEX Version 3)
361////////////////////////////////////////////////////////////////////////////
362const t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV3() {
363
364 while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
365
366 QString line = _stream->readLine();
367
368 if (line.isEmpty()) {
369 continue;
370 }
371
372 int flag = 0;
373 readInt(line, 31, 1, flag);
374 if (flag > 0) {
375 handleEpochFlag(flag, line);
376 continue;
377 }
378
379 QTextStream in(line.mid(1).toAscii(), QIODevice::ReadOnly);
380
381 // Epoch Time
382 // ----------
383 int year, month, day, hour, min;
384 double sec;
385 in >> year >> month >> day >> hour >> min >> sec;
386 _currEpo.tt.set(year, month, day, hour, min, sec);
387
388 // Number of Satellites
389 // --------------------
390 int numSat;
391 readInt(line, 32, 3, numSat);
392
393 _currEpo.rnxSat.resize(numSat);
394
395 // Observations
396 // ------------
397 for (int iSat = 0; iSat < numSat; iSat++) {
398 line = _stream->readLine();
399 _currEpo.rnxSat[iSat].satSys = line.toAscii()[0];
400 readInt(line, 1, 2, _currEpo.rnxSat[iSat].satNum);
401 char sys = line.toAscii()[0];
402 for (int iType = 0; iType < _header.nTypes(sys); iType++) {
403 int pos = 3 + 16*iType;
404 double obsValue = 0.0;
405 int lli = 0;
406 int snr = 0;
407 readDbl(line, pos, 14, obsValue);
408 readInt(line, pos + 14, 1, lli);
409 readInt(line, pos + 15, 1, snr);
410
411 if (_flgPowerFail) {
412 lli |= 1;
413 }
414
415 _currEpo.rnxSat[iSat].obs.push_back(obsValue);
416 _currEpo.rnxSat[iSat].lli.push_back(lli);
417 _currEpo.rnxSat[iSat].snr.push_back(snr);
418 }
419 }
420
421 _flgPowerFail = false;
422
423 return &_currEpo;
424 }
425
426 return 0;
427}
428
429// Retrieve single Epoch (RINEX Version 2)
430////////////////////////////////////////////////////////////////////////////
431const t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV2() {
432
433 while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
434
435 QString line = _stream->readLine();
436
437 if (line.isEmpty()) {
438 continue;
439 }
440
441 int flag = 0;
442 readInt(line, 28, 1, flag);
443 if (flag > 0) {
444 handleEpochFlag(flag, line);
445 continue;
446 }
447
448 QTextStream in(line.toAscii(), QIODevice::ReadOnly);
449
450 // Epoch Time
451 // ----------
452 int year, month, day, hour, min;
453 double sec;
454 in >> year >> month >> day >> hour >> min >> sec;
455 if (year < 80) {
456 year += 2000;
457 }
458 else if (year < 100) {
459 year += 1900;
460 }
461 _currEpo.tt.set(year, month, day, hour, min, sec);
462
463 // Number of Satellites
464 // --------------------
465 int numSat;
466 readInt(line, 29, 3, numSat);
467
468 _currEpo.rnxSat.resize(numSat);
469
470 // Read Satellite Numbers
471 // ----------------------
472 int pos = 32;
473 for (int iSat = 0; iSat < numSat; iSat++) {
474 if (iSat > 0 && iSat % 12 == 0) {
475 line = _stream->readLine();
476 pos = 32;
477 }
478
479 _currEpo.rnxSat[iSat].satSys = line.toAscii()[pos];
480 readInt(line, pos + 1, 2, _currEpo.rnxSat[iSat].satNum);
481
482 pos += 3;
483 }
484
485 // Read Observation Records
486 // ------------------------
487 for (int iSat = 0; iSat < numSat; iSat++) {
488 line = _stream->readLine();
489 pos = 0;
490 for (int iType = 0; iType < _header.nTypes(_currEpo.rnxSat[iSat].satSys); iType++) {
491 if (iType > 0 && iType % 5 == 0) {
492 line = _stream->readLine();
493 pos = 0;
494 }
495 double obsValue = 0.0;
496 int lli = 0;
497 int snr = 0;
498 readDbl(line, pos, 14, obsValue);
499 readInt(line, pos + 14, 1, lli);
500 readInt(line, pos + 15, 1, snr);
501
502 if (_flgPowerFail) {
503 lli |= 1;
504 }
505
506 _currEpo.rnxSat[iSat].obs.push_back(obsValue);
507 _currEpo.rnxSat[iSat].lli.push_back(lli);
508 _currEpo.rnxSat[iSat].snr.push_back(snr);
509
510 pos += 16;
511 }
512 }
513
514 _flgPowerFail = false;
515
516 return &_currEpo;
517 }
518
519 return 0;
520}
521
522// Set Header Information
523////////////////////////////////////////////////////////////////////////////
524void t_rnxObsFile::setHeader(const t_rnxObsHeader& header) {
525 _header._version = header._version;
526 _header._interval = header._interval;
527 _header._antennaName = header._antennaName;
528 _header._markerName = header._markerName;
529 _header._antNEU = header._antNEU;
530 _header._antXYZ = header._antXYZ;
531 _header._antBSG = header._antBSG;
532 _header._xyz = header._xyz;
533 for (unsigned iPrn = 1; iPrn <= MAXPRN_GPS; iPrn++) {
534 _header._wlFactorsL1[iPrn] = header._wlFactorsL1[iPrn];
535 _header._wlFactorsL2[iPrn] = header._wlFactorsL2[iPrn];
536 }
537 _header._startTime = header._startTime;
538 for (unsigned ii = 0; ii < header._obsTypesV2.size(); ii++) {
539 _header._obsTypesV2.push_back(header._obsTypesV2[ii]);
540 }
541 map<char, vector<QString> >::const_iterator it;
542 for (it = header._obsTypesV3.begin(); it != header._obsTypesV3.end(); it++) {
543 char sys = it->first;
544 const vector<QString>& typesV3 = it->second;
545 for (unsigned ii = 0; ii < typesV3.size(); ii++) {
546 _header._obsTypesV3[sys].push_back(typesV3[ii]);
547 }
548 }
549}
550
551// Write Header
552////////////////////////////////////////////////////////////////////////////
553void t_rnxObsFile::writeHeader() {
554 *_stream << QString("%1 Observation data Mixed")
555 .arg(_header._version, 9, 'f', 2)
556 .leftJustified(60)
557 << "RINEX VERSION / TYPE\n";
558
559 *_stream << QString("%1%2%3")
560 .arg("BNC "BNCVERSION, -20)
561 .arg("BKG", -20)
562 .arg(currentDateAndTimeGPS().date().toString("dd-MMM-yyyy"), -20)
563 .leftJustified(60)
564 << "PGM / RUN BY / DATE\n";
565
566 *_stream << QString("%1")
567 .arg(_header._markerName, -60)
568 .leftJustified(60)
569 << "MARKER NAME\n";
570
571 *_stream << QString("%1%2")
572 .arg("Observer", -20) // TODO
573 .arg("Agency", -40) // TODO
574 .leftJustified(60)
575 << "OBSERVER / AGENCY\n";
576
577 *_stream << QString("%1%2%3")
578 .arg("xxxx", -20) // TODO
579 .arg("Receiver", -20) // TODO
580 .arg("yyyy", -20) // TODO
581 .leftJustified(60)
582 << "REC # / TYPE / VERS\n";
583
584 *_stream << QString("%1%2")
585 .arg("xxxx", -20) // TODO
586 .arg(_header._antennaName, -20)
587 .leftJustified(60)
588 << "ANT # / TYPE\n";
589
590 *_stream << QString("%1%2%3")
591 .arg(_header._xyz(1), 14, 'f', 4)
592 .arg(_header._xyz(2), 14, 'f', 4)
593 .arg(_header._xyz(3), 14, 'f', 4)
594 .leftJustified(60)
595 << "APPROX POSITION XYZ\n";
596
597 *_stream << QString("%1%2%3")
598 .arg(_header._antNEU(3), 14, 'f', 4)
599 .arg(_header._antNEU(2), 14, 'f', 4)
600 .arg(_header._antNEU(1), 14, 'f', 4)
601 .leftJustified(60)
602 << "ANTENNA: DELTA H/E/N\n";
603
604 QString hlp;
605 QTextStream(&hlp) << QString("%1").arg(_header._obsTypesV2.size(), 6);
606 for (unsigned ii = 0; ii < _header._obsTypesV2.size(); ii++) {
607 QTextStream(&hlp) << QString("%1").arg(_header._obsTypesV2[ii], 6);
608 if (ii > 0 && (ii % 8 == 0 || ii == _header._obsTypesV2.size()-1)) {
609 *_stream << hlp.leftJustified(60) << "# / TYPES OF OBSERV\n";
610 hlp = QString().leftJusified(6);
611 }
612 }
613
614 *_stream << QString("%1")
615 .arg(_header._interval, 10, 'f', 3)
616 .leftJustified(60)
617 << "INTERVAL\n";
618
619 unsigned year, month, day, hour, min;
620 double sec;
621 _header._startTime.civil_date(year, month, day);
622 _header._startTime.civil_time(hour, min, sec);
623 *_stream << QString("%1%2%3%4%5%6%7")
624 .arg(year, 6)
625 .arg(month, 6)
626 .arg(day, 6)
627 .arg(hour, 6)
628 .arg(min, 6)
629 .arg(sec, 13, 'f', 7)
630 .arg("GPS", 8)
631 .leftJustified(60)
632 << "TIME OF FIRST OBS\n";
633
634 *_stream << QString()
635 .leftJustified(60)
636 << "END OF HEADER\n";
637}
638
639// Write Data Epoch
640////////////////////////////////////////////////////////////////////////////
641void t_rnxObsFile::writeEpoch(const t_rnxEpo* epo) {
642
643 unsigned year, month, day, hour, min;
644 double sec;
645 epo->tt.civil_date(year, month, day);
646 epo->tt.civil_time(hour, min, sec);
647
648 QString dateStr;
649 QTextStream(&dateStr) << QString(" %1%2%3%4%5%6")
650 .arg(int(fmod(year, 100)), 2, 10, QChar('0'))
651 .arg(month, 3)
652 .arg(day, 3)
653 .arg(hour, 3)
654 .arg(min, 3)
655 .arg(sec, 11, 'f', 7);
656
657 int flag = 0;
658 *_stream << dateStr
659 << QString("%1%2").arg(flag, 3).arg(epo->rnxSat.size(), 3);
660 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
661 if (iSat > 0 && iSat % 12 == 0) {
662 *_stream << endl << QString().leftJustified(32);
663 }
664 *_stream << epo->rnxSat[iSat].satSys
665 << QString("%1").arg(epo->rnxSat[iSat].satNum, 2);
666 }
667 *_stream << endl;
668 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
669 const t_rnxSat& rnxSat = epo->rnxSat[iSat];
670 for (unsigned iType = 0; iType < rnxSat.obs.size(); iType++) {
671 if (iType > 0 && iType % 5 == 0) {
672 *_stream << endl;
673 }
674 if (rnxSat.obs[iType] == 0.0) {
675 *_stream << QString().leftJustified(16);
676 }
677 else {
678 *_stream << QString("%1%2%3")
679 .arg(rnxSat.obs[iType], 14, 'f', 3)
680 .arg(rnxSat.lli[iType],1)
681 .arg(rnxSat.snr[iType],1);
682 }
683 }
684 *_stream << endl;
685 }
686}
Note: See TracBrowser for help on using the repository browser.