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

Last change on this file since 3853 was 3853, checked in by mervart, 12 years ago
File size: 17.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(value.toAscii(), QIODevice::ReadOnly);
145 int nTypes;
146 in >> nTypes;
147 _obsTypesV2.clear();
148 for (int ii = 0; ii < nTypes; ii++) {
149 QString hlp;
150 in >> hlp;
151 _obsTypesV2.push_back(hlp);
152 }
153 }
154 else if (key == "SYS / # / OBS TYPES") {
155 QTextStream* in = new QTextStream(value.toAscii(), QIODevice::ReadOnly);
156 char sys;
157 int nTypes;
158 *in >> sys >> nTypes;
159 _obsTypesV3[sys].clear();
160 for (int ii = 0; ii < nTypes; ii++) {
161 if (ii > 0 && ii % 13 == 0) {
162 line = stream->readLine(); ++numLines;
163 delete in;
164 in = new QTextStream(line.toAscii(), QIODevice::ReadOnly);
165 }
166 QString hlp;
167 *in >> hlp;
168 _obsTypesV3[sys].push_back(hlp);
169 }
170 delete in;
171 }
172 else if (key == "TIME OF FIRST OBS") {
173 QTextStream in(value.toAscii(), QIODevice::ReadOnly);
174 int year, month, day, hour, min;
175 double sec;
176 in >> year >> month >> day >> hour >> min >> sec;
177 _startTime.set(year, month, day, hour, min, sec);
178 }
179 if (maxLines > 0 && numLines == maxLines) {
180 break;
181 }
182 }
183
184 return success;
185}
186
187// Number of Observation Types (satellite-system specific)
188////////////////////////////////////////////////////////////////////////////
189int t_rnxObsFile::t_rnxObsHeader::nTypes(char sys) const {
190 if (_version < 3.0) {
191 return _obsTypesV2.size();
192 }
193 else {
194 map<char, vector<QString> >::const_iterator it = _obsTypesV3.find(sys);
195 if (it != _obsTypesV3.end()) {
196 return it->second.size();
197 }
198 else {
199 return 0;
200 }
201 }
202}
203
204// Observation Type (satellite-system specific)
205////////////////////////////////////////////////////////////////////////////
206const QString& t_rnxObsFile::t_rnxObsHeader::obsType(char sys, int index) const {
207 if (_version < 3.0) {
208 return _obsTypesV2.at(index);
209 }
210 else {
211 map<char, vector<QString> >::const_iterator it = _obsTypesV3.find(sys);
212 if (it != _obsTypesV3.end()) {
213 return it->second.at(index);
214 }
215 else {
216 return _emptyStr;
217 }
218 }
219}
220
221// Constructor
222////////////////////////////////////////////////////////////////////////////
223t_rnxObsFile::t_rnxObsFile(const QString& fileName, e_inpOut inpOut) {
224 _inpOut = inpOut;
225 _stream = 0;
226 _flgPowerFail = false;
227 if (_inpOut == input) {
228 openRead(fileName);
229 }
230 else {
231 openWrite(fileName);
232 }
233}
234
235// Open for input
236////////////////////////////////////////////////////////////////////////////
237void t_rnxObsFile::openRead(const QString& fileName) {
238
239 _fileName = fileName; expandEnvVar(_fileName);
240 _file = new QFile(_fileName);
241 _file->open(QIODevice::ReadOnly | QIODevice::Text);
242 _stream = new QTextStream();
243 _stream->setDevice(_file);
244
245 _header.read(_stream);
246
247 // Guess Observation Interval
248 // --------------------------
249 if (_header._interval == 0.0) {
250 bncTime ttPrev;
251 for (int iEpo = 0; iEpo < 10; iEpo++) {
252 const t_rnxEpo* rnxEpo = nextEpoch();
253 if (!rnxEpo) {
254 throw QString("t_rnxObsFile: not enough epochs");
255 }
256 if (iEpo > 0) {
257 double dt = rnxEpo->tt - ttPrev;
258 if (_header._interval == 0.0 || dt < _header._interval) {
259 _header._interval = dt;
260 }
261 }
262 ttPrev = rnxEpo->tt;
263 }
264 _stream->seek(0);
265 _header.read(_stream);
266 }
267
268 // Time of first observation
269 // -------------------------
270 if (!_header._startTime.valid()) {
271 const t_rnxEpo* rnxEpo = nextEpoch();
272 if (!rnxEpo) {
273 throw QString("t_rnxObsFile: not enough epochs");
274 }
275 _header._startTime = rnxEpo->tt;
276 _stream->seek(0);
277 _header.read(_stream);
278 }
279}
280
281// Open for output
282////////////////////////////////////////////////////////////////////////////
283void t_rnxObsFile::openWrite(const QString& fileName) {
284
285 _fileName = fileName; expandEnvVar(_fileName);
286 _file = new QFile(_fileName);
287 _file->open(QIODevice::WriteOnly | QIODevice::Text);
288 _stream = new QTextStream();
289 _stream->setDevice(_file);
290}
291
292// Destructor
293////////////////////////////////////////////////////////////////////////////
294t_rnxObsFile::~t_rnxObsFile() {
295 close();
296}
297
298// Close
299////////////////////////////////////////////////////////////////////////////
300void t_rnxObsFile::close() {
301 delete _stream; _stream = 0;
302 delete _file; _file = 0;
303}
304
305// Handle Special Epoch Flag
306////////////////////////////////////////////////////////////////////////////
307void t_rnxObsFile::handleEpochFlag(int flag, const QString& line) {
308
309 // Power Failure
310 // -------------
311 if (flag == 1) {
312 _flgPowerFail = true;
313 }
314
315 // Start moving antenna
316 // --------------------
317 else if (flag == 2) {
318 // no action
319 }
320
321 // Re-Read Header
322 // --------------
323 else if (flag == 3 || flag == 4) {
324 int numLines = 0;
325 if (version() < 3.0) {
326 readInt(line, 29, 3, numLines);
327 }
328 else {
329 readInt(line, 32, 3, numLines);
330 }
331 _header.read(_stream, numLines);
332 }
333
334 // Unhandled Flag
335 // --------------
336 else {
337 throw QString("t_rnxObsFile: unhandled flag\n" + line);
338 }
339}
340
341// Retrieve single Epoch
342////////////////////////////////////////////////////////////////////////////
343const t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpoch() {
344
345 _currEpo.clear();
346
347 if (version() < 3.0) {
348 return nextEpochV2();
349 }
350 else {
351 return nextEpochV3();
352 }
353}
354
355// Retrieve single Epoch (RINEX Version 3)
356////////////////////////////////////////////////////////////////////////////
357const t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV3() {
358
359 while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
360
361 QString line = _stream->readLine();
362
363 if (line.isEmpty()) {
364 continue;
365 }
366
367 int flag = 0;
368 readInt(line, 31, 1, flag);
369 if (flag > 0) {
370 handleEpochFlag(flag, line);
371 continue;
372 }
373
374 QTextStream in(line.mid(1).toAscii(), QIODevice::ReadOnly);
375
376 // Epoch Time
377 // ----------
378 int year, month, day, hour, min;
379 double sec;
380 in >> year >> month >> day >> hour >> min >> sec;
381 _currEpo.tt.set(year, month, day, hour, min, sec);
382
383 // Number of Satellites
384 // --------------------
385 int numSat;
386 readInt(line, 32, 3, numSat);
387
388 _currEpo.rnxSat.resize(numSat);
389
390 // Observations
391 // ------------
392 for (int iSat = 0; iSat < numSat; iSat++) {
393 line = _stream->readLine();
394 _currEpo.rnxSat[iSat].satSys = line.toAscii()[0];
395 readInt(line, 1, 2, _currEpo.rnxSat[iSat].satNum);
396 char sys = line.toAscii()[0];
397 for (int iType = 0; iType < _header.nTypes(sys); iType++) {
398 int pos = 3 + 16*iType;
399 double obsValue = 0.0;
400 int lli = 0;
401 int snr = 0;
402 readDbl(line, pos, 14, obsValue);
403 readInt(line, pos + 14, 1, lli);
404 readInt(line, pos + 15, 1, snr);
405
406 if (_flgPowerFail) {
407 lli |= 1;
408 }
409
410 _currEpo.rnxSat[iSat].obs.push_back(obsValue);
411 _currEpo.rnxSat[iSat].lli.push_back(lli);
412 _currEpo.rnxSat[iSat].snr.push_back(snr);
413 }
414 }
415
416 _flgPowerFail = false;
417
418 return &_currEpo;
419 }
420
421 return 0;
422}
423
424// Retrieve single Epoch (RINEX Version 2)
425////////////////////////////////////////////////////////////////////////////
426const t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV2() {
427
428 while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
429
430 QString line = _stream->readLine();
431
432 if (line.isEmpty()) {
433 continue;
434 }
435
436 int flag = 0;
437 readInt(line, 28, 1, flag);
438 if (flag > 0) {
439 handleEpochFlag(flag, line);
440 continue;
441 }
442
443 QTextStream in(line.toAscii(), QIODevice::ReadOnly);
444
445 // Epoch Time
446 // ----------
447 int year, month, day, hour, min;
448 double sec;
449 in >> year >> month >> day >> hour >> min >> sec;
450 if (year < 80) {
451 year += 2000;
452 }
453 else if (year < 100) {
454 year += 1900;
455 }
456 _currEpo.tt.set(year, month, day, hour, min, sec);
457
458 // Number of Satellites
459 // --------------------
460 int numSat;
461 readInt(line, 29, 3, numSat);
462
463 _currEpo.rnxSat.resize(numSat);
464
465 // Read Satellite Numbers
466 // ----------------------
467 int pos = 32;
468 for (int iSat = 0; iSat < numSat; iSat++) {
469 if (iSat > 0 && iSat % 12 == 0) {
470 line = _stream->readLine();
471 pos = 32;
472 }
473
474 _currEpo.rnxSat[iSat].satSys = line.toAscii()[pos];
475 readInt(line, pos + 1, 2, _currEpo.rnxSat[iSat].satNum);
476
477 pos += 3;
478 }
479
480 // Read Observation Records
481 // ------------------------
482 for (int iSat = 0; iSat < numSat; iSat++) {
483 line = _stream->readLine();
484 pos = 0;
485 for (int iType = 0; iType < _header.nTypes(_currEpo.rnxSat[iSat].satSys); iType++) {
486 if (iType > 0 && iType % 5 == 0) {
487 line = _stream->readLine();
488 pos = 0;
489 }
490 double obsValue = 0.0;
491 int lli = 0;
492 int snr = 0;
493 readDbl(line, pos, 14, obsValue);
494 readInt(line, pos + 14, 1, lli);
495 readInt(line, pos + 15, 1, snr);
496
497 if (_flgPowerFail) {
498 lli |= 1;
499 }
500
501 _currEpo.rnxSat[iSat].obs.push_back(obsValue);
502 _currEpo.rnxSat[iSat].lli.push_back(lli);
503 _currEpo.rnxSat[iSat].snr.push_back(snr);
504
505 pos += 16;
506 }
507 }
508
509 _flgPowerFail = false;
510
511 return &_currEpo;
512 }
513
514 return 0;
515}
516
517// Set Header Information
518////////////////////////////////////////////////////////////////////////////
519void t_rnxObsFile::setHeader(const t_rnxObsHeader& header) {
520 _header._version = header._version;
521 _header._interval = header._interval;
522 _header._antennaName = header._antennaName;
523 _header._markerName = header._markerName;
524 _header._antNEU = header._antNEU;
525 _header._antXYZ = header._antXYZ;
526 _header._antBSG = header._antBSG;
527 _header._xyz = header._xyz;
528 for (unsigned iPrn = 1; iPrn <= MAXPRN_GPS; iPrn++) {
529 _header._wlFactorsL1[iPrn] = header._wlFactorsL1[iPrn];
530 _header._wlFactorsL2[iPrn] = header._wlFactorsL2[iPrn];
531 }
532 _header._startTime = header._startTime;
533 for (unsigned ii = 0; ii < header._obsTypesV2.size(); ii++) {
534 _header._obsTypesV2.push_back(header._obsTypesV2[ii]);
535 }
536 map<char, vector<QString> >::const_iterator it;
537 for (it = header._obsTypesV3.begin(); it != header._obsTypesV3.end(); it++) {
538 char sys = it->first;
539 const vector<QString>& typesV3 = it->second;
540 for (unsigned ii = 0; ii < typesV3.size(); ii++) {
541 _header._obsTypesV3[sys].push_back(typesV3[ii]);
542 }
543 }
544}
545
546// Write Header
547////////////////////////////////////////////////////////////////////////////
548void t_rnxObsFile::writeHeader() {
549 *_stream << QString("%1 Observation data Mixed")
550 .arg(_header._version, 9, 'f', 2)
551 .leftJustified(60)
552 << "RINEX VERSION / TYPE\n";
553
554 *_stream << QString("%1%2%3")
555 .arg("BNC "BNCVERSION, -20)
556 .arg("BKG", -20)
557 .arg(currentDateAndTimeGPS().date().toString("dd-MMM-yyyy"), -20)
558 .leftJustified(60)
559 << "PGM / RUN BY / DATE\n";
560
561 *_stream << QString("%1")
562 .arg(_header._markerName, -60)
563 .leftJustified(60)
564 << "MARKER NAME\n";
565
566 *_stream << QString("%1%2")
567 .arg("Observer", -20) // TODO
568 .arg("Agency", -40) // TODO
569 .leftJustified(60)
570 << "OBSERVER / AGENCY\n";
571
572 *_stream << QString("%1%2%3")
573 .arg("xxxx", -20) // TODO
574 .arg("Receiver", -20) // TODO
575 .arg("yyyy", -20) // TODO
576 .leftJustified(60)
577 << "REC # / TYPE / VERS\n";
578
579 *_stream << QString("%1%2")
580 .arg("xxxx", -20) // TODO
581 .arg(_header._antennaName, -20)
582 .leftJustified(60)
583 << "ANT # / TYPE\n";
584
585 *_stream << QString("%1%2%3")
586 .arg(_header._xyz(1), 14, 'f', 4)
587 .arg(_header._xyz(2), 14, 'f', 4)
588 .arg(_header._xyz(3), 14, 'f', 4)
589 .leftJustified(60)
590 << "APPROX POSITION XYZ\n";
591
592 *_stream << QString("%1%2%3")
593 .arg(_header._antNEU(3), 14, 'f', 4)
594 .arg(_header._antNEU(2), 14, 'f', 4)
595 .arg(_header._antNEU(1), 14, 'f', 4)
596 .leftJustified(60)
597 << "ANTENNA: DELTA H/E/N\n";
598
599 QString hlp;
600 for (unsigned ii = 0; ii < _header._obsTypesV2.size(); ii++) {
601 hlp += QString("%1").arg(_header._obsTypesV2[ii], 6);
602 }
603 *_stream << (QString("%1").arg(_header._obsTypesV2.size(),6) + hlp)
604 .leftJustified(60)
605 << "# / TYPES OF OBSERV\n";
606
607 *_stream << QString()
608 .leftJustified(60)
609 << "END OF HEADER\n";
610}
611
612// Write Data Epoch
613////////////////////////////////////////////////////////////////////////////
614void t_rnxObsFile::writeEpoch(const t_rnxEpo* epo) {
615 *_stream << "Epoch " << epo->tt.datestr().c_str() << " "
616 << epo->tt.timestr().c_str() << endl;
617}
Note: See TracBrowser for help on using the repository browser.