source: ntrip/trunk/BNC/src/rinex/rnxobsfile.cpp@ 10460

Last change on this file since 10460 was 9963, checked in by stuerze, 2 years ago

minor changes to be msvc compatible

File size: 51.3 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 "bnccore.h"
47#include "bncsettings.h"
48
49using namespace std;
50
51// Constructor
52////////////////////////////////////////////////////////////////////////////
53t_rnxObsHeader::t_rnxObsHeader() {
54 _usedSystems = "GREJCSI";
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 <= t_prn::MAXPRN_GPS; iPrn++) {
62 _wlFactorsL1[iPrn] = 1;
63 _wlFactorsL2[iPrn] = 1;
64 }
65 bncSettings settings;
66 _writeRinexOnlyWithSklObsTypes = settings.value("rnxOnlyWithSKL").toBool();
67
68}
69
70// Destructor
71////////////////////////////////////////////////////////////////////////////
72t_rnxObsHeader::~t_rnxObsHeader() {
73}
74
75double t_rnxObsHeader::versionFromInt(int version) {
76 switch (version) {
77 case 2:
78 return defaultRnxObsVersion2;
79 case 3:
80 return defaultRnxObsVersion3;
81 case 4:
82 return defaultRnxObsVersion4;
83 default:
84 return defaultRnxObsVersion3;
85 }
86}
87
88
89// Read Header
90////////////////////////////////////////////////////////////////////////////
91t_irc t_rnxObsHeader::read(QTextStream* stream, int maxLines) {
92 _comments.clear();
93 int numLines = 0;
94
95 while ( stream->status() == QTextStream::Ok && !stream->atEnd() ) {
96 QString line = stream->readLine(); ++ numLines;
97 if (line.isEmpty()) {
98 continue;
99 }
100 if (line.indexOf("END OF FILE") != -1) {
101 break;
102 }
103 QString value = line.mid(0,60).trimmed();
104 QString key = line.mid(60).trimmed();
105 if (key == "END OF HEADER") {
106 break;
107 }
108 else if (key == "RINEX VERSION / TYPE") {
109 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
110 in >> _version;
111 }
112 else if (key == "PGM / RUN BY / DATE") {
113 _runByDate.append(value.trimmed());
114 }
115 else if (key == "MARKER NAME") {
116 _markerName = value;
117 }
118 else if (key == "MARKER TYPE") {
119 _markerType = value;
120 }
121 else if (key == "MARKER NUMBER") {
122 _markerNumber = line.mid(0,20).trimmed();
123 }
124 else if (key == "ANT # / TYPE") {
125 _antennaNumber = line.mid( 0,20).trimmed();
126 _antennaName = line.mid(20,20).trimmed();
127 }
128 else if (key == "OBSERVER / AGENCY") {
129 _observer = line.mid( 0,20).trimmed();
130 _agency = line.mid(20,40).trimmed();
131 }
132 else if (key == "REC # / TYPE / VERS") {
133 _receiverNumber = line.mid( 0,20).trimmed();
134 _receiverType = line.mid(20,20).trimmed();
135 _receiverVersion = line.mid(40,20).trimmed();
136 }
137 else if (key == "INTERVAL") {
138 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
139 in >> _interval;
140 }
141 else if (key == "COMMENT") {
142 _comments << line.mid(0,60).trimmed();
143 }
144 else if (key == "WAVELENGTH FACT L1/2") {
145 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
146 int wlFactL1 = 0;
147 int wlFactL2 = 0;
148 int numSat = 0;
149 in >> wlFactL1 >> wlFactL2 >> numSat;
150 if (numSat == 0) {
151 for (unsigned iPrn = 1; iPrn <= t_prn::MAXPRN_GPS; iPrn++) {
152 _wlFactorsL1[iPrn] = wlFactL1;
153 _wlFactorsL2[iPrn] = wlFactL2;
154 }
155 }
156 else {
157 for (int ii = 0; ii < numSat; ii++) {
158 QString prn; in >> prn;
159 if (prn[0] == 'G') {
160 int iPrn;
161 readInt(prn, 1, 2, iPrn);
162 _wlFactorsL1[iPrn] = wlFactL1;
163 _wlFactorsL2[iPrn] = wlFactL2;
164 }
165 }
166 }
167 }
168 else if (key == "APPROX POSITION XYZ") {
169 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
170 in >> _xyz[0] >> _xyz[1] >> _xyz[2];
171 }
172 else if (key == "ANTENNA: DELTA H/E/N") {
173 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
174 in >> _antNEU[2] >> _antNEU[1] >> _antNEU[0];
175 }
176 else if (key == "ANTENNA: DELTA X/Y/Z") {
177 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
178 in >> _antXYZ[0] >> _antXYZ[1] >> _antXYZ[2];
179 }
180 else if (key == "ANTENNA: B.SIGHT XYZ") {
181 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
182 in >> _antBSG[0] >> _antBSG[1] >> _antBSG[2];
183 }
184 else if (key == "DOI") {
185 _digitalObjectId = line.mid(0,60).trimmed();
186 }
187 else if (key == "LICENSE OF USE") {
188 _licenseOfUse.append(line.mid(0,60).trimmed());
189 }
190 else if (key == "STATION INFORMATION") {
191 _stationInformation.append(line.mid(0,60).trimmed());
192 }
193 else if (key == "# / TYPES OF OBSERV") {
194 if (_version == 0.0) {
195 _version = defaultRnxObsVersion2;
196 }
197 QTextStream* in = new QTextStream(value.toLatin1(), QIODevice::ReadOnly);
198 int nTypes;
199 *in >> nTypes;
200 char sys0 = _usedSystems[0].toLatin1();
201 _obsTypes[sys0].clear();
202 for (int ii = 0; ii < nTypes; ii++) {
203 if (ii > 0 && ii % 9 == 0) {
204 line = stream->readLine(); ++numLines;
205 delete in;
206 in = new QTextStream(line.left(60).toLatin1(), QIODevice::ReadOnly);
207 }
208 QString hlp;
209 *in >> hlp;
210 _obsTypes[sys0].append(hlp);
211 }
212 for (int ii = 1; ii < _usedSystems.length(); ii++) {
213 char sysI = _usedSystems[ii].toLatin1();
214 _obsTypes[sysI] = _obsTypes[sys0];
215 }
216 }
217 else if (key == "SYS / # / OBS TYPES") {
218 if (_version == 0.0) {
219 _version = defaultRnxObsVersion3;
220 }
221 QTextStream* in = new QTextStream(value.toLatin1(), QIODevice::ReadOnly);
222 char sys;
223 int nTypes;
224 *in >> sys >> nTypes;
225 _obsTypes[sys].clear();
226 for (int ii = 0; ii < nTypes; ii++) {
227 if (ii > 0 && ii % 13 == 0) {
228 line = stream->readLine(); ++numLines;
229 delete in;
230 in = new QTextStream(line.toLatin1(), QIODevice::ReadOnly);
231 }
232 QString hlp;
233 *in >> hlp;
234 if (sys == 'C' && _version < 3.03) {
235 hlp.replace('1', '2');
236 }
237 _obsTypes[sys].push_back(hlp);
238 }
239 delete in;
240 }
241 else if (key == "TIME OF FIRST OBS") {
242 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
243 int year, month, day, hour, min;
244 double sec;
245 in >> year >> month >> day >> hour >> min >> sec;
246 _startTime.set(year, month, day, hour, min, sec);
247 }
248 else if (key == "SYS / PHASE SHIFT"){
249 QTextStream* in = new QTextStream(value.toLatin1(), QIODevice::ReadOnly);
250 char sys;
251 QString obstype;
252 double shift;
253 int satnum = 0;
254 QStringList satList;
255 QString sat;
256 *in >> sys >> obstype >> shift >> satnum;
257 if (obstype.size()) {
258 for (int ii = 0; ii < satnum; ii++) {
259 if (ii > 0 && ii % 10 == 0) {
260 line = stream->readLine(); ++numLines;
261 delete in;
262 in = new QTextStream(line.left(60).toLatin1(), QIODevice::ReadOnly);
263 }
264 *in >> sat;
265 satList.append(sat);
266 }
267 delete in;
268 }
269 _phaseShifts.insert(sys+obstype, QPair<double, QStringList>(shift, satList));
270 }
271 else if (key == "GLONASS COD/PHS/BIS"){
272 QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
273 for (int ii = 0; ii < 4; ii++) {
274 QString type;
275 double value;
276 in >> type >> value;
277 if (type.size())
278 _gloBiases[type] = value;
279 }
280 }
281 else if (key == "GLONASS SLOT / FRQ #") {
282 QTextStream* in = new QTextStream(value.toLatin1(), QIODevice::ReadOnly);
283 int nSlots = 0;
284 *in >> nSlots;
285 for (int ii = 0; ii < nSlots; ii++) {
286 if (ii > 0 && ii % 8 == 0) {
287 line = stream->readLine(); ++numLines;
288 delete in;
289 in = new QTextStream(line.left(60).toLatin1(), QIODevice::ReadOnly);
290 }
291 QString sat;
292 int slot;
293 *in >> sat >> slot;
294 t_prn prn;
295 prn.set(sat.toStdString());
296 if(sat.size())
297 _gloSlots[prn] = slot;
298 }
299 delete in;
300 }
301 if (maxLines > 0 && numLines == maxLines) {
302 break;
303 }
304 }
305
306 // set default observation types if empty in input file
307 // ----------------------------------------------------
308 if (_obsTypes.empty()) {
309 if (!_writeRinexOnlyWithSklObsTypes) {
310 setDefault(_markerName, _version);
311 }
312 else {
313 return failure;
314 }
315 }
316
317 // Systems used
318 // ------------
319 _usedSystems.clear();
320 QMapIterator<char, QStringList> it(_obsTypes);
321 while (it.hasNext()) {
322 it.next();
323 _usedSystems += QChar(it.key());
324 }
325
326 return success;
327}
328
329// Set Default Header
330////////////////////////////////////////////////////////////////////////////
331void t_rnxObsHeader::setDefault(const QString& markerName, int version) {
332
333 _markerName = markerName;
334
335 _version = versionFromInt(version);
336
337 _comments << "Default set of observation types used";
338 _comments.removeDuplicates();
339
340 _obsTypes.clear();
341 if (_version < 3.0) {
342 _obsTypes['G'] << "C1" << "P1" << "L1" << "S1"
343 << "C2" << "P2" << "L2" << "S2";
344 _obsTypes['R'] = _obsTypes['G'];
345 _obsTypes['E'] = _obsTypes['G'];
346 _obsTypes['J'] = _obsTypes['G'];
347 _obsTypes['S'] = _obsTypes['G'];
348 _obsTypes['C'] = _obsTypes['G'];
349 }
350 else {
351 _obsTypes['G'] << "C1C" << "L1C" << "S1C"
352 << "C1W" << "L1W" << "S1W"
353 << "C2X" << "L2X" << "S2X"
354 << "C2W" << "L2W" << "S2W"
355 << "C5X" << "L5X" << "S5X";
356
357 _obsTypes['J'] << "C1C" << "L1C" << "S1C"
358 << "C1Z" << "L1Z" << "S1Z"
359 << "C1X" << "L1X" << "S1X"
360 << "C2L" << "L2L" << "S2L"
361 << "C2X" << "L2X" << "S2X"
362 << "C5Q" << "L5Q" << "S5Q"
363 << "C5X" << "L5X" << "S5X"
364 << "C6L" << "L6L" << "S6L";
365
366 _obsTypes['R'] << "C1C" << "L1C" << "S1C"
367 << "C1P" << "L1P" << "S1P"
368 << "C2C" << "L2C" << "S2C"
369 << "C2P" << "L2P" << "S2P"
370 << "C3I" << "L3I" << "S3I"
371 << "C4X" << "L4X" << "S4X"
372 << "C6X" << "L6X" << "S6X";
373
374 _obsTypes['E'] << "C1C" << "L1C" << "S1C"
375 << "C1X" << "L1X" << "S1X"
376 << "C5Q" << "L5Q" << "S5Q"
377 << "C5X" << "L5X" << "S5X"
378 << "C6C" << "L6C" << "S6C"
379 << "C6X" << "L6X" << "S6X"
380 << "C7Q" << "L7Q" << "S7Q"
381 << "C7X" << "L7X" << "S7X"
382 << "C8Q" << "L8Q" << "S8Q"
383 << "C8X" << "L8X" << "S8X";
384
385 _obsTypes['S'] << "C1C" << "L1C" << "S1C"
386 << "C5I" << "L5I" << "S5I"
387 << "C5Q" << "L5Q" << "S5Q"
388 << "C5X" << "L5X" << "S5X";
389
390 _obsTypes['C'] << "C2I" << "L2I" << "S2I"
391 << "C2Q" << "L2Q" << "S2Q"
392 << "C2X" << "L2X" << "S2X"
393 << "C6I" << "L6I" << "S6I"
394 << "C6Q" << "L6Q" << "S6Q"
395 << "C6X" << "L6X" << "S6X"
396 << "C7I" << "L7I" << "S7I"
397 << "C7Q" << "L7Q" << "S7Q"
398 << "C7X" << "L7X" << "S7X";
399
400 _obsTypes['I'] << "C5A" << "L5A" << "S5A"
401 << "C9A" << "L9A" << "S9A";
402 }
403}
404
405// Copy header
406////////////////////////////////////////////////////////////////////////////
407void t_rnxObsHeader::set(const t_rnxObsHeader& header, int version,
408 const QStringList* useObsTypes,
409 const QStringList* phaseShifts,
410 const QStringList* gloBiases,
411 const QStringList* gloSlots,
412 const QStringList* runByDate) {
413
414 _version = versionFromInt(version);
415
416 _interval = header._interval;
417 _antennaNumber = header._antennaNumber;
418 _antennaName = header._antennaName;
419 _markerName = header._markerName;
420 _markerNumber = header._markerNumber;
421 _markerType = header._markerType;
422 _antNEU = header._antNEU;
423 _antXYZ = header._antXYZ;
424 _antBSG = header._antBSG;
425 _xyz = header._xyz;
426 _observer = header._observer;
427 _agency = header._agency;
428 _receiverNumber = header._receiverNumber;
429 _receiverType = header._receiverType;
430 _receiverVersion = header._receiverVersion;
431 _startTime = header._startTime;
432 _usedSystems = header._usedSystems;
433 _comments = header._comments;
434 _comments.removeDuplicates();
435
436 for (unsigned iPrn = 1; iPrn <= t_prn::MAXPRN_GPS; iPrn++) {
437 _wlFactorsL1[iPrn] = header._wlFactorsL1[iPrn];
438 _wlFactorsL2[iPrn] = header._wlFactorsL2[iPrn];
439 }
440
441 // Set observation types
442 // ---------------------
443 _obsTypes.clear();
444 if (!useObsTypes || useObsTypes->size() == 0) {
445 if (int(_version) == int(header._version)) {
446 _obsTypes = header._obsTypes;
447 }
448 else {
449 if (_version >= 3.0) {
450 _comments << "Default set of observation types used";
451 _comments.removeDuplicates();
452 _obsTypes['G'] << "C1C" << "L1C" << "S1C"
453 << "C1W" << "L1W" << "S1W"
454 << "C2X" << "L2X" << "S2X"
455 << "C2W" << "L2W" << "S2W"
456 << "C5X" << "L5X" << "S5X";
457
458 _obsTypes['J'] << "C1C" << "L1C" << "S1C"
459 << "C1S" << "L1S" << "S1S"
460 << "C1L" << "L1L" << "S1L"
461 << "C1X" << "L1X" << "S1X"
462 << "C2S" << "L2S" << "S2S"
463 << "C2L" << "L2L" << "S2L"
464 << "C2X" << "L2X" << "S2X"
465 << "C5X" << "L5X" << "S5X";
466
467 _obsTypes['R'] << "C1C" << "L1C" << "S1C"
468 << "C1P" << "L1P" << "S1P"
469 << "C2C" << "L2C" << "S2C"
470 << "C2P" << "L2P" << "S2P";
471
472 _obsTypes['E'] << "C1X" << "L1X" << "S1X"
473 << "C5X" << "L5X" << "S5X"
474 << "C7X" << "L7X" << "S7X"
475 << "C8X" << "L8X" << "S8X";
476
477 _obsTypes['S'] << "C1C" << "L1C" << "S1C"
478 << "C5I" << "L5I" << "S5I"
479 << "C5Q" << "L5Q" << "S5Q";
480
481 _obsTypes['C'] << "C2I" << "L2I" << "S2I"
482 << "C6I" << "L6I" << "S6I"
483 << "C7I" << "L7I" << "S7I";
484
485 _obsTypes['I'] << "C5A" << "L5A" << "S5A"
486 << "C9A" << "L9A" << "S9A";
487 }
488 else {
489 for (int iSys = 0; iSys < header.numSys(); iSys++) {
490 char sys = header.system(iSys);
491 for (int iType = 0; iType < header.nTypes(sys); iType++) {
492 QString type = header.obsType(sys, iType, _version);
493 for (int jSys = 0; jSys < _usedSystems.length(); jSys++) {
494 char thisSys = _usedSystems[jSys].toLatin1();
495 if (!_obsTypes[thisSys].contains(type)) {
496 _obsTypes[thisSys].push_back(type);
497 }
498 }
499 }
500 }
501 }
502 }
503 }
504 else {
505 for (int iType = 0; iType < useObsTypes->size(); iType++) {
506 if (useObsTypes->at(iType).indexOf(":") != -1) {
507 QStringList hlp = useObsTypes->at(iType).split(":", QString::SkipEmptyParts);
508 if (hlp.size() == 2 && hlp[0].length() == 1) {
509 if (_version >= 3.0) {
510 char sys = hlp[0][0].toLatin1();
511 QString type = t_rnxObsFile::type2to3(sys, hlp[1]);
512 if (!_obsTypes[sys].contains(type)) {
513 _obsTypes[sys].push_back(type);
514 }
515 }
516 else {
517 for (int iSys = 0; iSys < _usedSystems.length(); iSys++) {
518 char sys = _usedSystems[iSys].toLatin1();
519 QString type = t_rnxObsFile::type3to2(sys, hlp[1]);
520 if (!_obsTypes[sys].contains(type)) {
521 _obsTypes[sys].push_back(type);
522 }
523 }
524 }
525 }
526 }
527 else {
528 for (int iSys = 0; iSys < _usedSystems.length(); iSys++) {
529 char sys = _usedSystems[iSys].toLatin1();
530 QString type = _version >= 3.0 ? t_rnxObsFile::type2to3(sys, useObsTypes->at(iType)) :
531 t_rnxObsFile::type3to2(sys, useObsTypes->at(iType));
532 if (!_obsTypes[sys].contains(type)) {
533 _obsTypes[sys].push_back(type);
534 }
535 }
536 }
537 }
538 _usedSystems.clear();
539 QMapIterator<char, QStringList> it(_obsTypes);
540 while (it.hasNext()) {
541 it.next();
542 _usedSystems += QChar(it.key());
543 }
544 }
545
546 if (_version >= 3.0) {
547 // set phase shifts
548 if (!phaseShifts || phaseShifts->empty()) {
549 _phaseShifts = header._phaseShifts;
550 }
551 else {
552 foreach (const QString &str, *phaseShifts) {
553 QStringList hlp = str.split("_", QString::SkipEmptyParts);
554 QStringList hlp1 = hlp.last().split(":", QString::SkipEmptyParts);
555 QString type = hlp.first();
556 double shift = hlp1.first().toDouble();
557 hlp1.removeFirst();
558 QString satStr = hlp1.join(" ");
559 if (satStr.size()) {
560 hlp1 = satStr.split(" ");
561 }
562 QStringList &satList = hlp1;
563 QMap<QString, QPair<double, QStringList> >::iterator it = _phaseShifts.find(type);
564 if ( it != _phaseShifts.end()) {
565 it.value().second.append(satList);
566 it.value().second.removeDuplicates();
567 }
568 else {
569 _phaseShifts.insert(type, QPair<double, QStringList>(shift, satList));
570 }
571 }
572 }
573 // set GLONASS biases
574 if (!gloBiases || gloBiases->empty()) {
575 _gloBiases = header._gloBiases;
576 }
577 else {
578 foreach (const QString &str, *gloBiases) {
579 QStringList hlp = str.split(":", QString::SkipEmptyParts);
580 QString type = hlp.first();;
581 double value = hlp.last().toDouble();
582 if (type.size())
583 _gloBiases[type] = value;
584 }
585 }
586 // set GLONASS slots
587 if (!gloSlots || gloSlots->empty()) {
588 _gloSlots = header._gloSlots;
589 }
590 else {
591 foreach (const QString &str, *gloSlots) {
592 QStringList hlp = str.split(":", QString::SkipEmptyParts);
593 QString sat = hlp.first();
594 int slot = hlp.last().toInt();
595 t_prn prn;
596 prn.set(sat.toStdString());
597 if(sat.size())
598 _gloSlots[prn] = slot;
599 }
600 }
601 }
602 if (_version >= 4.0) {
603 // set run by date entries
604 if (!runByDate || runByDate->empty()) {
605 _runByDate = header._runByDate;
606 }
607 else {
608 foreach (const QString &str, *runByDate) {
609 _runByDate.append(str);
610 }
611 }
612 }
613
614}
615
616// Write Header
617////////////////////////////////////////////////////////////////////////////
618void t_rnxObsHeader::write(QTextStream* stream,
619 const QMap<QString, QString>* txtMap) const {
620
621 QStringList newComments;
622 QStringList runByDate;
623 QString runBy = BNC_CORE->userName();
624
625 if (txtMap) {
626 QMapIterator<QString, QString> it(*txtMap);
627 while (it.hasNext()) {
628 it.next();
629 if (it.key() == "RUN BY") {
630 runBy = it.value();
631 }
632 else if (it.key() == "RUN BY DATE") {
633 runByDate = it.value().split("\\n", QString::SkipEmptyParts);
634 }
635 else if ((it.key().indexOf("COMMENT")) != -1) {
636 newComments += it.value().split("\\n", QString::SkipEmptyParts);
637 }
638 }
639 newComments.removeDuplicates();
640 }
641
642 *stream << QString("%1 OBSERVATION DATA M")
643 .arg(_version, 9, 'f', 2)
644 .leftJustified(60)
645 << "RINEX VERSION / TYPE\n";
646
647 const QString fmtDate = (_version < 3.0) ? "dd-MMM-yy hh:mm"
648 : "yyyyMMdd hhmmss UTC";
649 *stream << QString("%1%2%3")
650 .arg(BNC_CORE->pgmName(), -20)
651 .arg(runBy.trimmed().left(20), -20)
652 .arg(QDateTime::currentDateTime().toUTC().toString(fmtDate), -20)
653 .leftJustified(60)
654 << "PGM / RUN BY / DATE\n";
655
656 if (_version >= 4.0) {
657 QStringListIterator itRunByDt(_runByDate);
658 while (itRunByDt.hasNext()) {
659 *stream << itRunByDt.next().trimmed().left(60).leftJustified(60)
660 << "PGM / RUN BY / DATE\n";
661 }
662 }
663 QStringListIterator itCmnt(_comments + newComments);
664 while (itCmnt.hasNext()) {
665 *stream << itCmnt.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
666 }
667 QString markerName = _markerName.left(9);
668 if (_version < 3.0) {
669 markerName = _markerName.left(4);
670 }
671 *stream << QString("%1")
672 .arg(markerName, -60)
673 .leftJustified(60)
674 << "MARKER NAME\n";
675
676
677 if (!_markerNumber.isEmpty()) {
678 *stream << QString("%1")
679 .arg(_markerNumber, -20)
680 .leftJustified(60)
681 << "MARKER NUMBER\n";
682 }
683
684 if (_version >= 3.0) {
685 *stream << QString("%1")
686 .arg(_markerType, -60)
687 .leftJustified(60)
688 << "MARKER TYPE\n";
689 }
690
691 *stream << QString("%1%2")
692 .arg(_observer, -20)
693 .arg(_agency, -40)
694 .leftJustified(60)
695 << "OBSERVER / AGENCY\n";
696
697 *stream << QString("%1%2%3")
698 .arg(_receiverNumber, -20)
699 .arg(_receiverType, -20)
700 .arg(_receiverVersion, -20)
701 .leftJustified(60)
702 << "REC # / TYPE / VERS\n";
703
704 *stream << QString("%1%2")
705 .arg(_antennaNumber, -20)
706 .arg(_antennaName, -20)
707 .leftJustified(60)
708 << "ANT # / TYPE\n";
709
710 *stream << QString("%1%2%3")
711 .arg(_xyz(1), 14, 'f', 4)
712 .arg(_xyz(2), 14, 'f', 4)
713 .arg(_xyz(3), 14, 'f', 4)
714 .leftJustified(60)
715 << "APPROX POSITION XYZ\n";
716
717 *stream << QString("%1%2%3")
718 .arg(_antNEU(3), 14, 'f', 4)
719 .arg(_antNEU(2), 14, 'f', 4)
720 .arg(_antNEU(1), 14, 'f', 4)
721 .leftJustified(60)
722 << "ANTENNA: DELTA H/E/N\n";
723
724 if (_version < 3.0) {
725 int defaultWlFact1 = _wlFactorsL1[1];
726 int defaultWlFact2 = _wlFactorsL2[1]; // TODO check all prns
727 *stream << QString("%1%2")
728 .arg(defaultWlFact1, 6)
729 .arg(defaultWlFact2, 6)
730 .leftJustified(60)
731 << "WAVELENGTH FACT L1/2\n";
732 }
733
734 *stream << obsTypesStrings().join("");
735
736 if (_interval > 0) {
737 *stream << QString("%1")
738 .arg(_interval, 10, 'f', 3)
739 .leftJustified(60)
740 << "INTERVAL\n";
741 }
742
743 unsigned year, month, day, hour, min;
744 double sec;
745 _startTime.civil_date(year, month, day);
746 _startTime.civil_time(hour, min, sec);
747 *stream << QString("%1%2%3%4%5%6%7")
748 .arg(year, 6)
749 .arg(month, 6)
750 .arg(day, 6)
751 .arg(hour, 6)
752 .arg(min, 6)
753 .arg(sec, 13, 'f', 7)
754 .arg("GPS", 8)
755 .leftJustified(60)
756 << "TIME OF FIRST OBS\n";
757
758 if (_version >= 3.0) {
759 if (_phaseShifts.empty()) {
760 QMap<char, QStringList>::const_iterator it;
761 for (it = _obsTypes.begin(); it != _obsTypes.end(); ++it) {
762 char sys = it.key();
763 double shift = 0.0;
764 foreach (const QString &obstype, it.value()) {
765 if (obstype.left(1).contains('L')) {
766 *stream << QString("%1%2%3")
767 .arg(sys, 0)
768 .arg(obstype, 4)
769 .arg(shift, 9, 'f', 5)
770 .leftJustified(60)
771 << "SYS / PHASE SHIFT\n";
772 }
773 }
774 }
775 } else {
776 QMap<QString, QPair<double, QStringList> >::const_iterator it;
777 QString emptyFillStr;
778 for(it = _phaseShifts.begin(); it!= _phaseShifts.end(); ++it) {
779 QString sys = it.key().left(1);
780 QString obstype = it.key().mid(1);
781 double shift = it.value().first;
782 QStringList satList = it.value().second;
783 satList.sort();
784 QString hlp;
785 if (obstype.isEmpty()) {
786 hlp = QString("%1")
787 .arg(sys.toStdString().c_str(), 0);
788 }
789 else {
790 hlp = QString("%1%2")
791 .arg(sys.toStdString().c_str(), 0)
792 .arg(obstype, 4);
793 }
794 if (shift) {
795 hlp += QString("%1")
796 .arg(shift, 9, 'f', 5);
797 }
798 if (!satList.empty()) {
799 hlp += QString("%1").arg(satList.size(), 4);
800 }
801 else {
802 *stream << QString("%1")
803 .arg(hlp, 0)
804 .leftJustified(60)
805 << "SYS / PHASE SHIFT\n";
806 hlp = "";
807 }
808 int ii = 0;
809 QStringList::const_iterator it_s;
810 for (it_s = satList.begin(); it_s != satList.end(); ++it_s) {
811 (hlp.contains(obstype)) ?
812 emptyFillStr = "": emptyFillStr = " ";
813 hlp += QString("%1").arg(*it_s, 4);
814 ii++;
815 if (ii % 10 == 0) {
816 *stream << QString("%1%2")
817 .arg(emptyFillStr, 0)
818 .arg(hlp, 0)
819 .leftJustified(60)
820 << "SYS / PHASE SHIFT\n";
821 hlp = "";
822 }
823 }
824 if (hlp.size()) {
825 (hlp.contains(obstype)) ?
826 emptyFillStr = "": emptyFillStr = " ";
827 *stream << QString("%1%2")
828 .arg(emptyFillStr, 0)
829 .arg(hlp, 0)
830 .leftJustified(60)
831 << "SYS / PHASE SHIFT\n";
832 }
833 }
834 }
835 }
836
837 if (_version >= 3.0) {
838 QString hlp = "";
839 QMap<QString, double>::const_iterator it = _gloBiases.begin();
840 while (it != _gloBiases.end()){
841 hlp += QString("%1%2").arg(it.key(), 4).arg(it.value(), 9, 'f', 3);
842 it++;
843 }
844 *stream << QString("%1")
845 .arg(hlp, 0)
846 .leftJustified(60)
847 << "GLONASS COD/PHS/BIS\n";
848 }
849
850 if (_version >= 3.0) {
851 QString number = QString::number(_gloSlots.size());
852 QString hlp = "";
853 int ii = 0;
854 QMap<t_prn, int>::const_iterator it = _gloSlots.begin();
855 while (it != _gloSlots.end()) {
856 QString prn(it.key().toString().c_str());
857 hlp += QString("%1%2").arg(prn, 4).arg(it.value(), 3);
858 it++;
859 ii++;
860 if (ii % 8 == 0) {
861 *stream << QString("%1%2")
862 .arg(number, 3)
863 .arg(hlp, 0)
864 .leftJustified(60)
865 << "GLONASS SLOT / FRQ #\n";
866 ii = 0;
867 hlp = number = "";
868 }
869 }
870 if (hlp.size() || !_gloSlots.size()) {
871 *stream << QString("%1%2")
872 .arg(number, 3)
873 .arg(hlp, 0)
874 .leftJustified(60)
875 << "GLONASS SLOT / FRQ #\n";
876 }
877 }
878
879 *stream << QString()
880 .leftJustified(60)
881 << "END OF HEADER\n";
882}
883
884// Number of Different Systems
885////////////////////////////////////////////////////////////////////////////
886int t_rnxObsHeader::numSys() const {
887 return _obsTypes.size();
888}
889
890//
891////////////////////////////////////////////////////////////////////////////
892char t_rnxObsHeader::system(int iSys) const {
893 int iSysLocal = -1;
894 QMapIterator<char, QStringList> it(_obsTypes);
895 while (it.hasNext()) {
896 ++iSysLocal;
897 it.next();
898 if (iSysLocal == iSys) {
899 return it.key();
900 }
901 }
902 return ' ';
903}
904
905//
906////////////////////////////////////////////////////////////////////////////
907QString t_rnxObsHeader::usedSystems(void) const {
908 return _usedSystems;
909}
910
911//
912////////////////////////////////////////////////////////////////////////////
913QStringList t_rnxObsHeader::obsTypes(char sys) const {
914 if (_obsTypes.contains(sys)) {
915 return _obsTypes[sys];
916 }
917 else {
918 return QStringList();
919 }
920}
921
922// Number of Observation Types (satellite-system specific)
923////////////////////////////////////////////////////////////////////////////
924int t_rnxObsHeader::nTypes(char sys) const {
925 if (_obsTypes.contains(sys)) {
926 return _obsTypes[sys].size();
927 }
928 else {
929 return 0;
930 }
931}
932
933// Number of GLONASS biases
934////////////////////////////////////////////////////////////////////////////
935int t_rnxObsHeader::numGloBiases() const {
936 return _gloBiases.size();
937}
938
939// Number of GLONASS slots
940////////////////////////////////////////////////////////////////////////////
941int t_rnxObsHeader::numGloSlots() const {
942 return _gloSlots.size();
943}
944
945// Observation Type (satellite-system specific)
946////////////////////////////////////////////////////////////////////////////
947QString t_rnxObsHeader::obsType(char sys, int index, double version) const {
948 if (version == 0.0) {
949 version = _version;
950 }
951
952 if (_obsTypes.contains(sys)) {
953 QString origType = _obsTypes[sys].at(index);
954 if (int(version) == int(_version)) {
955 return origType;
956 }
957 else if (int(version) == 2) {
958 return t_rnxObsFile::type3to2(sys, origType);
959 }
960 else {
961 return t_rnxObsFile::type2to3(sys, origType);
962 }
963 }
964 return "";
965}
966
967//
968////////////////////////////////////////////////////////////////////////////
969QStringList t_rnxObsHeader::phaseShifts() const {
970 QStringList strList;
971 QMap<QString, QPair<double, QStringList> >::const_iterator it = _phaseShifts.begin();
972 while (it != _phaseShifts.end()) {
973 strList.append(QString("%1_%2:%3").arg(it.key(), 3).arg(it.value().first, 9, 'f', 3).arg(it.value().second.join(" ")));
974 it++;
975 }
976 return strList;
977}
978
979//
980////////////////////////////////////////////////////////////////////////////
981QStringList t_rnxObsHeader::gloBiases() const {
982 QStringList strList;
983 QMap<QString, double>::const_iterator it = _gloBiases.begin();
984 while (it != _gloBiases.end()) {
985 strList.append(QString("%1:%2").arg(it.key(), 3).arg(it.value(), 9, 'f', 3));
986 it++;
987 }
988 return strList;
989}
990
991//
992////////////////////////////////////////////////////////////////////////////
993QStringList t_rnxObsHeader::gloSlots() const {
994 QStringList strList;
995 QMap<t_prn, int>::const_iterator it = _gloSlots.begin();
996 while (it != _gloSlots.end()){
997 QString prn(it.key().toString().c_str());
998 strList.append(QString("%1:%2").arg(prn, 3).arg(it.value()));
999 it++;
1000 }
1001 return strList;
1002}
1003
1004// Write Observation Types
1005////////////////////////////////////////////////////////////////////////////
1006QStringList t_rnxObsHeader::obsTypesStrings() const {
1007
1008 QStringList strList;
1009 if (_version < 3.0) {
1010 char sys0 = _usedSystems[0].toLatin1();
1011 QString hlp;
1012 QTextStream(&hlp) << QString("%1").arg(_obsTypes[sys0].size(), 6);
1013 for (int ii = 0; ii < _obsTypes[sys0].size(); ii++) {
1014 QTextStream(&hlp) << QString("%1").arg(_obsTypes[sys0][ii], 6);
1015 if ((ii+1) % 9 == 0 || ii == _obsTypes[sys0].size()-1) {
1016 strList.append(hlp.leftJustified(60) + "# / TYPES OF OBSERV\n");
1017 hlp = QString().leftJustified(6);
1018 }
1019 }
1020 }
1021 else {
1022 for (int iSys = 0; iSys < numSys(); iSys++) {
1023 char sys = system(iSys);
1024 QString hlp;
1025 QTextStream(&hlp) << QString("%1 %2").arg(sys).arg(nTypes(sys), 3);
1026 for (int iType = 0; iType < nTypes(sys); iType++) {
1027 QString type = obsType(sys, iType);
1028 QTextStream(&hlp) << QString(" %1").arg(type, -3);
1029 if ((iType+1) % 13 == 0 || iType == nTypes(sys)-1) {
1030 strList.append(hlp.leftJustified(60) + "SYS / # / OBS TYPES\n");
1031 hlp = QString().leftJustified(6);
1032 }
1033 }
1034 }
1035 }
1036
1037 return strList;
1038}
1039
1040// Constructor
1041////////////////////////////////////////////////////////////////////////////
1042t_rnxObsFile::t_rnxObsFile(const QString& fileName, e_inpOut inpOut) {
1043 _inpOut = inpOut;
1044 _stream = 0;
1045 _flgPowerFail = false;
1046 if (_inpOut == input) {
1047 openRead(fileName);
1048 }
1049 else {
1050 openWrite(fileName);
1051 }
1052}
1053
1054// Open for input
1055////////////////////////////////////////////////////////////////////////////
1056void t_rnxObsFile::openRead(const QString& fileName) {
1057
1058 _fileName = fileName; expandEnvVar(_fileName);
1059 _file = new QFile(_fileName);
1060 _file->open(QIODevice::ReadOnly | QIODevice::Text);
1061 _stream = new QTextStream();
1062 _stream->setDevice(_file);
1063
1064 _header.read(_stream);
1065
1066 // Guess Observation Interval
1067 // --------------------------
1068 if (_header._interval == 0.0) {
1069 bncTime ttPrev;
1070 for (int iEpo = 0; iEpo < 10; iEpo++) {
1071 const t_rnxEpo* rnxEpo = nextEpoch();
1072 if (!rnxEpo) {
1073 throw QString("t_rnxObsFile: not enough epochs");
1074 }
1075 if (iEpo > 0) {
1076 double dt = rnxEpo->tt - ttPrev;
1077 if (_header._interval == 0.0 || dt < _header._interval) {
1078 _header._interval = dt;
1079 }
1080 }
1081 ttPrev = rnxEpo->tt;
1082 }
1083 _stream->seek(0);
1084 _header.read(_stream);
1085 }
1086
1087 // Time of first observation
1088 // -------------------------
1089 if (!_header._startTime.valid()) {
1090 const t_rnxEpo* rnxEpo = nextEpoch();
1091 if (!rnxEpo) {
1092 throw QString("t_rnxObsFile: not enough epochs");
1093 }
1094 _header._startTime = rnxEpo->tt;
1095 _stream->seek(0);
1096 _header.read(_stream);
1097 }
1098}
1099
1100// Open for output
1101////////////////////////////////////////////////////////////////////////////
1102void t_rnxObsFile::openWrite(const QString& fileName) {
1103
1104 _fileName = fileName; expandEnvVar(_fileName);
1105 _file = new QFile(_fileName);
1106 _file->open(QIODevice::WriteOnly | QIODevice::Text);
1107 _stream = new QTextStream();
1108 _stream->setDevice(_file);
1109}
1110
1111// Destructor
1112////////////////////////////////////////////////////////////////////////////
1113t_rnxObsFile::~t_rnxObsFile() {
1114 close();
1115}
1116
1117// Close
1118////////////////////////////////////////////////////////////////////////////
1119void t_rnxObsFile::close() {
1120 delete _stream; _stream = 0;
1121 delete _file; _file = 0;
1122}
1123
1124// Handle Special Epoch Flag
1125////////////////////////////////////////////////////////////////////////////
1126void t_rnxObsFile::handleEpochFlag(int flag, const QString& line,
1127 bool& headerReRead) {
1128
1129 headerReRead = false;
1130
1131 // Power Failure
1132 // -------------
1133 if (flag == 1) {
1134 _flgPowerFail = true;
1135 }
1136
1137 // Start moving antenna
1138 // --------------------
1139 else if (flag == 2) {
1140 // no action
1141 }
1142
1143 // Re-Read Header
1144 // --------------
1145 else if (flag == 3 || flag == 4 || flag == 5) {
1146 int numLines = 0;
1147 if (version() < 3.0) {
1148 readInt(line, 29, 3, numLines);
1149 }
1150 else {
1151 readInt(line, 32, 3, numLines);
1152 }
1153 if (flag == 3 || flag == 4) {
1154 _header.read(_stream, numLines);
1155 headerReRead = true;
1156 }
1157 else {
1158 for (int ii = 0; ii < numLines; ii++) {
1159 _stream->readLine();
1160 }
1161 }
1162 }
1163
1164 // Unhandled Flag
1165 // --------------
1166 else {
1167 throw QString("t_rnxObsFile: unhandled flag\n" + line);
1168 }
1169}
1170
1171// Retrieve single Epoch
1172////////////////////////////////////////////////////////////////////////////
1173t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpoch() {
1174 _currEpo.clear();
1175 if (version() < 3.0) {
1176 return nextEpochV2();
1177 }
1178 else {
1179 return nextEpochV3();
1180 }
1181}
1182
1183// Retrieve single Epoch (RINEX Version 3)
1184////////////////////////////////////////////////////////////////////////////
1185t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV3() {
1186
1187 while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
1188
1189 QString line = _stream->readLine();
1190
1191 if (line.isEmpty()) {
1192 continue;
1193 }
1194
1195 int flag = 0;
1196 readInt(line, 31, 1, flag);
1197 if (flag > 0) {
1198 bool headerReRead = false;
1199 handleEpochFlag(flag, line, headerReRead);
1200 if (headerReRead) {
1201 continue;
1202 }
1203 }
1204
1205 QTextStream in(line.mid(1).toLatin1(), QIODevice::ReadOnly);
1206
1207 // Epoch Time
1208 // ----------
1209 int year, month, day, hour, min;
1210 double sec;
1211 in >> year >> month >> day >> hour >> min >> sec;
1212 _currEpo.tt.set(year, month, day, hour, min, sec);
1213
1214 // Number of Satellites
1215 // --------------------
1216 int numSat;
1217 readInt(line, 32, 3, numSat);
1218
1219 _currEpo.rnxSat.resize(numSat);
1220
1221 // Observations
1222 // ------------
1223 for (int iSat = 0; iSat < numSat; iSat++) {
1224 line = _stream->readLine();
1225 t_prn prn; prn.set(line.left(3).toLatin1().data());
1226 _currEpo.rnxSat[iSat].prn = prn;
1227 char sys = prn.system();
1228 for (int iType = 0; iType < _header.nTypes(sys); iType++) {
1229 int pos = 3 + 16*iType;
1230 double obsValue = 0.0;
1231 int lli = 0;
1232 int snr = 0;
1233 readDbl(line, pos, 14, obsValue);
1234 readInt(line, pos + 14, 1, lli);
1235 readInt(line, pos + 15, 1, snr);
1236 if (_flgPowerFail) {
1237 lli |= 1;
1238 }
1239 QString type = obsType(sys, iType);
1240 _currEpo.rnxSat[iSat].obs[type].value = obsValue;
1241 _currEpo.rnxSat[iSat].obs[type].lli = lli;
1242 _currEpo.rnxSat[iSat].obs[type].snr = snr;
1243 }
1244 }
1245
1246 _flgPowerFail = false;
1247
1248 return &_currEpo;
1249 }
1250
1251 return 0;
1252}
1253
1254// Retrieve single Epoch (RINEX Version 2)
1255////////////////////////////////////////////////////////////////////////////
1256t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV2() {
1257
1258 while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
1259
1260 QString line = _stream->readLine();
1261
1262 if (line.isEmpty()) {
1263 continue;
1264 }
1265
1266 int flag = 0;
1267 readInt(line, 28, 1, flag);
1268 if (flag > 0) {
1269 bool headerReRead = false;
1270 handleEpochFlag(flag, line, headerReRead);
1271 if (headerReRead) {
1272 continue;
1273 }
1274 }
1275
1276 QTextStream in(line.toLatin1(), QIODevice::ReadOnly);
1277
1278 // Epoch Time
1279 // ----------
1280 int year, month, day, hour, min;
1281 double sec;
1282 in >> year >> month >> day >> hour >> min >> sec;
1283 if (year < 80) {
1284 year += 2000;
1285 }
1286 else if (year < 100) {
1287 year += 1900;
1288 }
1289 _currEpo.tt.set(year, month, day, hour, min, sec);
1290
1291 // Number of Satellites
1292 // --------------------
1293 int numSat;
1294 readInt(line, 29, 3, numSat);
1295
1296 _currEpo.rnxSat.resize(numSat);
1297
1298 // Read Satellite Numbers
1299 // ----------------------
1300 int pos = 32;
1301 for (int iSat = 0; iSat < numSat; iSat++) {
1302 if (iSat > 0 && iSat % 12 == 0) {
1303 line = _stream->readLine();
1304 pos = 32;
1305 }
1306
1307 char sys = line.toLatin1()[pos];
1308 if (sys == ' ') {
1309 sys = 'G';
1310 }
1311 int satNum; readInt(line, pos + 1, 2, satNum);
1312 _currEpo.rnxSat[iSat].prn.set(sys, satNum);
1313
1314 pos += 3;
1315 }
1316
1317 // Read Observation Records
1318 // ------------------------
1319 for (int iSat = 0; iSat < numSat; iSat++) {
1320 char sys = _currEpo.rnxSat[iSat].prn.system();
1321 line = _stream->readLine();
1322 pos = 0;
1323 for (int iType = 0; iType < _header.nTypes(sys); iType++) {
1324 if (iType > 0 && iType % 5 == 0) {
1325 line = _stream->readLine();
1326 pos = 0;
1327 }
1328 double obsValue = 0.0;
1329 int lli = 0;
1330 int snr = 0;
1331 readDbl(line, pos, 14, obsValue);
1332 readInt(line, pos + 14, 1, lli);
1333 readInt(line, pos + 15, 1, snr);
1334
1335 if (_flgPowerFail) {
1336 lli |= 1;
1337 }
1338
1339 QString type = obsType(sys, iType);
1340 _currEpo.rnxSat[iSat].obs[type].value = obsValue;
1341 _currEpo.rnxSat[iSat].obs[type].lli = lli;
1342 _currEpo.rnxSat[iSat].obs[type].snr = snr;
1343
1344 pos += 16;
1345 }
1346 }
1347
1348 _flgPowerFail = false;
1349
1350 return &_currEpo;
1351 }
1352
1353 return 0;
1354}
1355
1356// Write Data Epoch
1357////////////////////////////////////////////////////////////////////////////
1358void t_rnxObsFile::writeEpoch(const t_rnxEpo* epo) {
1359 if (epo == 0) {
1360 return;
1361 }
1362 t_rnxEpo epoLocal;
1363 epoLocal.tt = epo->tt;
1364 for (unsigned ii = 0; ii < epo->rnxSat.size(); ii++) {
1365 const t_rnxSat& rnxSat = epo->rnxSat[ii];
1366 if (_header._obsTypes[rnxSat.prn.system()].size() > 0) {
1367 if (_header.version() < 3.0) { // exclude new GNSS such as BDS, QZSS, IRNSS, etc.
1368 if (rnxSat.prn.system() != 'G' && rnxSat.prn.system() != 'R' &&
1369 rnxSat.prn.system() != 'E' && rnxSat.prn.system() != 'S' &&
1370 rnxSat.prn.system() != 'I') {
1371 continue;
1372 }
1373 }
1374 epoLocal.rnxSat.push_back(rnxSat);
1375 }
1376 }
1377 std::stable_sort(epoLocal.rnxSat.begin(), epoLocal.rnxSat.end(), t_rnxSat::prnSort);
1378
1379 if (version() < 3.0) {
1380 return writeEpochV2(_stream, _header, &epoLocal);
1381 }
1382 else {
1383 return writeEpochV3(_stream, _header, &epoLocal);
1384 }
1385}
1386
1387// Write Data Epoch (RINEX Version 2)
1388////////////////////////////////////////////////////////////////////////////
1389void t_rnxObsFile::writeEpochV2(QTextStream* stream, const t_rnxObsHeader& header,
1390 const t_rnxEpo* epo) {
1391
1392 unsigned year, month, day, hour, min;
1393 double sec;
1394 epo->tt.civil_date(year, month, day);
1395 epo->tt.civil_time(hour, min, sec);
1396
1397 QString dateStr;
1398 QTextStream(&dateStr) << QString(" %1 %2 %3 %4 %5%6")
1399 .arg(int(fmod(double(year), 100.0)), 2, 10, QChar('0'))
1400 .arg(month, 2, 10, QChar('0'))
1401 .arg(day, 2, 10, QChar('0'))
1402 .arg(hour, 2, 10, QChar('0'))
1403 .arg(min, 2, 10, QChar('0'))
1404 .arg(sec, 11, 'f', 7);
1405
1406 int flag = 0;
1407 *stream << dateStr << QString("%1%2").arg(flag, 3).arg(epo->rnxSat.size(), 3);
1408 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1409 const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1410 if (iSat > 0 && iSat % 12 == 0) {
1411 *stream << endl << QString().leftJustified(32);
1412 }
1413 *stream << rnxSat.prn.toString().c_str();
1414 }
1415 *stream << endl;
1416
1417 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1418 const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1419 char sys = rnxSat.prn.system();
1420 for (int iTypeV2 = 0; iTypeV2 < header.nTypes(sys); iTypeV2++) {
1421 if (iTypeV2 > 0 && iTypeV2 % 5 == 0) {
1422 *stream << endl;
1423 }
1424 QString typeV2 = header.obsType(sys, iTypeV2);
1425 bool found = false;
1426 QStringList preferredAttribList = signalPriorities(sys);
1427 QString preferredAttrib;
1428 for (int ii = 0; ii < preferredAttribList.size(); ii++) {
1429 if (preferredAttribList[ii].indexOf("&") != -1) {
1430 QStringList hlp = preferredAttribList[ii].split("&", QString::SkipEmptyParts);
1431 if (hlp.size() == 2 && hlp[0].contains(typeV2[1])) {
1432 preferredAttrib = hlp[1];
1433 }
1434 }
1435 else {
1436 preferredAttrib = preferredAttribList[ii];
1437 }
1438 }
1439
1440 for (int iPref = 0; iPref < preferredAttrib.size(); iPref++) {
1441 QMapIterator<QString, t_rnxObs> itObs(rnxSat.obs);
1442 while (itObs.hasNext()) {
1443 itObs.next();
1444 const QString& type = itObs.key();
1445 const t_rnxObs& rnxObs = itObs.value();
1446 if ( preferredAttrib[iPref] == '?' ||
1447 (type.length() == 2 && preferredAttrib[iPref] == '_' ) ||
1448 (type.length() == 3 && preferredAttrib[iPref] == type[2]) ) {
1449 if (typeV2 == type3to2(sys, type)) {
1450 found = true;
1451 if (rnxObs.value == 0.0) {
1452 *stream << QString().leftJustified(16);
1453 }
1454 else {
1455 *stream << QString("%1").arg(rnxObs.value, 14, 'f', 3);
1456 if (rnxObs.lli != 0.0) {
1457 *stream << QString("%1").arg(rnxObs.lli,1);
1458 }
1459 else {
1460 *stream << ' ';
1461 }
1462 if (rnxObs.snr != 0.0) {
1463 *stream << QString("%1").arg(rnxObs.snr,1);
1464 }
1465 else {
1466 *stream << ' ';
1467 }
1468 }
1469 goto end_loop_iPref;
1470 }
1471 }
1472 }
1473 } end_loop_iPref:
1474 if (!found) {
1475 *stream << QString().leftJustified(16);
1476 }
1477 }
1478 *stream << endl;
1479 }
1480}
1481
1482// Write Data Epoch (RINEX Version 3)
1483////////////////////////////////////////////////////////////////////////////
1484void t_rnxObsFile::writeEpochV3(QTextStream* stream, const t_rnxObsHeader& header,
1485 const t_rnxEpo* epo) {
1486
1487 unsigned year, month, day, hour, min;
1488 double sec;
1489 epo->tt.civil_date(year, month, day);
1490 epo->tt.civil_time(hour, min, sec);
1491
1492 QString dateStr;
1493 QTextStream(&dateStr) << QString("> %1 %2 %3 %4 %5%6")
1494 .arg(year, 4)
1495 .arg(month, 2, 10, QChar('0'))
1496 .arg(day, 2, 10, QChar('0'))
1497 .arg(hour, 2, 10, QChar('0'))
1498 .arg(min, 2, 10, QChar('0'))
1499 .arg(sec, 11, 'f', 7);
1500
1501 int flag = 0;
1502 *stream << dateStr << QString("%1%2\n").arg(flag, 3).arg(epo->rnxSat.size(), 3);
1503
1504 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1505 const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1506 char sys = rnxSat.prn.system();
1507
1508 std::vector <const t_rnxObs*> hlp(header.nTypes(sys));
1509 for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
1510 hlp[iTypeV3] = 0;
1511 QString typeV3 = header.obsType(sys, iTypeV3);
1512 QMapIterator<QString, t_rnxObs> itObs(rnxSat.obs);
1513
1514 // Exact match
1515 // -----------
1516 while (itObs.hasNext()) {
1517 itObs.next();
1518 const QString& type = itObs.key();
1519 const t_rnxObs& rnxObs = itObs.value();
1520 if (typeV3 == type2to3(sys, type) && rnxObs.value != 0.0) {
1521 hlp[iTypeV3] = &itObs.value();
1522 }
1523 }
1524
1525 // Non-Exact match
1526 // ---------------
1527 itObs.toFront();
1528 while (itObs.hasNext()) {
1529 itObs.next();
1530 const QString& type = itObs.key();
1531 const t_rnxObs& rnxObs = itObs.value();
1532 if (hlp[iTypeV3] == 0 && typeV3 == type2to3(sys, type).left(2) && rnxObs.value != 0.0) {
1533 hlp[iTypeV3] = &itObs.value();
1534 }
1535 }
1536 }
1537
1538 if (header.nTypes(sys)) {
1539 *stream << rnxSat.prn.toString().c_str();
1540 for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
1541 const t_rnxObs* rnxObs = hlp[iTypeV3];
1542 if (rnxObs == 0) {
1543 *stream << QString().leftJustified(16);
1544 }
1545 else {
1546 *stream << QString("%1").arg(rnxObs->value, 14, 'f', 3);
1547 if (rnxObs->lli != 0.0) {
1548 *stream << QString("%1").arg(rnxObs->lli, 1);
1549 }
1550 else {
1551 *stream << ' ';
1552 }
1553 if (rnxObs->snr != 0.0) {
1554 *stream << QString("%1").arg(rnxObs->snr, 1);
1555 }
1556 else {
1557 *stream << ' ';
1558 }
1559 }
1560 }
1561 *stream << endl;
1562 }
1563 }
1564}
1565
1566// Translate Observation Type v2 --> v3
1567////////////////////////////////////////////////////////////////////////////
1568QString t_rnxObsFile::type2to3(char sys, const QString& typeV2) {
1569 if (typeV2 == "P1") {
1570 return (sys == 'G') ? "C1W" : "C1P";
1571 }
1572 else if (typeV2 == "P2") {
1573 return (sys == 'G') ? "C2W" : "C2P";
1574 }
1575 return typeV2;
1576}
1577
1578// Translate Observation Type v3 --> v2
1579////////////////////////////////////////////////////////////////////////////
1580QString t_rnxObsFile::type3to2(char /* sys */, const QString& typeV3) {
1581 if (typeV3 == "C1P" || typeV3 == "C1W") {
1582 return "P1";
1583 }
1584 else if (typeV3 == "C2P" || typeV3 == "C2W") {
1585 return "P2";
1586 }
1587 return typeV3.left(2);
1588}
1589
1590// Set Observations from RINEX File
1591////////////////////////////////////////////////////////////////////////////
1592void t_rnxObsFile::setObsFromRnx(const t_rnxObsFile* rnxObsFile, const t_rnxObsFile::t_rnxEpo* epo,
1593 const t_rnxObsFile::t_rnxSat& rnxSat, t_satObs& obs) {
1594 obs._staID = rnxObsFile->markerName().toLatin1().constData();
1595 obs._prn = rnxSat.prn;
1596 obs._time = epo->tt;
1597
1598 char sys = rnxSat.prn.system();
1599
1600 QChar addToL2;
1601 for (int iType = 0; iType < rnxObsFile->nTypes(sys); iType++) {
1602 QString type = rnxObsFile->obsType(sys, iType);
1603 QString typeV3 = rnxObsFile->obsType(sys, iType, 3.0); // may or may not differ from type
1604 if (rnxSat.obs.contains(type) && rnxSat.obs[type].value != 0.0) {
1605 if (type == "P2" && typeV3.length() > 2) {
1606 addToL2 = typeV3[2];
1607 break;
1608 }
1609 }
1610 }
1611
1612 for (int iType = 0; iType < rnxObsFile->nTypes(sys); iType++) {
1613 QString type = rnxObsFile->obsType(sys, iType);
1614 QString typeV3 = rnxObsFile->obsType(sys, iType, 3.0); // may or may not differ from type
1615 if (type == "L2") {
1616 typeV3 += addToL2;
1617 }
1618 if (rnxSat.obs.contains(type)) {
1619 const t_rnxObs& rnxObs = rnxSat.obs[type];
1620 if (rnxObs.value != 0.0) {
1621 string type2ch(typeV3.mid(1).toLatin1().data());
1622
1623 t_frqObs* frqObs = 0;
1624 for (unsigned iFrq = 0; iFrq < obs._obs.size(); iFrq++) {
1625 if (obs._obs[iFrq]->_rnxType2ch == type2ch) {
1626 frqObs = obs._obs[iFrq];
1627 break;
1628 }
1629 }
1630 if (frqObs == 0) {
1631 frqObs = new t_frqObs;
1632 frqObs->_rnxType2ch = type2ch;
1633 obs._obs.push_back(frqObs);
1634 }
1635
1636 switch( typeV3.toLatin1().data()[0] ) {
1637 case 'C':
1638 frqObs->_codeValid = true;
1639 frqObs->_code = rnxObs.value;
1640 break;
1641 case 'L':
1642 frqObs->_phaseValid = true;
1643 frqObs->_phase = rnxObs.value;
1644 frqObs->_slip = (rnxObs.lli & 1);
1645 break;
1646 case 'D':
1647 frqObs->_dopplerValid = true;
1648 frqObs->_doppler = rnxObs.value;
1649 break;
1650 case 'S':
1651 frqObs->_snrValid = true;
1652 frqObs->_snr = rnxObs.value;
1653 break;
1654 }
1655
1656 // Handle old-fashioned SNR values
1657 // -------------------------------
1658 if (rnxObs.snr != 0 && !frqObs->_snrValid) {
1659 frqObs->_snrValid = true;
1660 frqObs->_snr = rnxObs.snr * 6.0 + 2.5;
1661 }
1662 }
1663 }
1664 }
1665}
1666
1667// Tracking Mode Priorities
1668////////////////////////////////////////////////////////////////////////////
1669QStringList t_rnxObsFile::signalPriorities(char sys) {
1670
1671 bncSettings settings;
1672
1673 QStringList priorList;
1674 QString reqcAction = settings.value("reqcAction").toString();
1675
1676 // Priorities in Edit/Concatenate (post processing) mode
1677 // ---------------------------------------------------
1678 if (reqcAction == "Edit/Concatenate") {
1679 priorList = settings.value("reqcV2Priority").toString().split(" ", QString::SkipEmptyParts);
1680 }
1681
1682 // Priorities in real-time mode
1683 // ----------------------------
1684 else {
1685 priorList = settings.value("rnxV2Priority").toString().split(" ", QString::SkipEmptyParts);
1686 }
1687
1688 QStringList result;
1689 for (int ii = 0; ii < priorList.size(); ii++) {
1690 if (priorList[ii].indexOf(":") != -1) {
1691 QStringList hlp = priorList[ii].split(":", QString::SkipEmptyParts);
1692 if (hlp.size() == 2 && hlp[0].length() == 1 && hlp[0][0] == sys) {
1693 result.append(hlp[1]);
1694 }
1695 }
1696 else {
1697 result.append(priorList[ii]);
1698 }
1699 }
1700
1701 if (result.empty()) {
1702 switch (sys) {
1703 case 'G':
1704 result.append("12&PWCSLXYN");
1705 result.append("5&IQX");
1706 break;
1707 case 'R':
1708 result.append("12&PC");
1709 result.append("3&IQX");
1710 break;
1711 case 'E':
1712 result.append("16&BCX");
1713 result.append("578&IQX");
1714 break;
1715 case 'J':
1716 result.append("1&SLXCZ");
1717 result.append("26&SLX");
1718 result.append("5&IQX");
1719 break;
1720 case 'C':
1721 result.append("IQX");
1722 break;
1723 case 'I':
1724 result.append("ABCX");
1725 break;
1726 case 'S':
1727 result.append("1&C");
1728 result.append("5&IQX");
1729 break;
1730 }
1731 }
1732 return result;
1733}
Note: See TracBrowser for help on using the repository browser.