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

Last change on this file since 10979 was 10979, checked in by stuerze, 3 weeks ago

RINEX version 2 files can be written now from skl files containing RINEX version 3/4 observation types too

File size: 52.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 _configuredVersion = settings.value("rnxVersion").toInt();
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 = versionFromInt(_configuredVersion);
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 if (_version < 3.0) {
447 // Sanitize: a source header labeled v2 could still carry malformed
448 // (e.g. leftover 3-char) codes; always force them through type3to2.
449 QMap<char, QStringList>::const_iterator it = header._obsTypes.constBegin();
450 for ( ; it != header._obsTypes.constEnd(); ++it) {
451 char sys = it.key();
452 for (int iType = 0; iType < it.value().size(); iType++) {
453 QString conv = t_rnxObsFile::type3to2(sys, it.value().at(iType));
454 if (!_obsTypes[sys].contains(conv)) {
455 _obsTypes[sys].push_back(conv);
456 }
457 }
458 }
459 }
460 else {
461 _obsTypes = header._obsTypes;
462 }
463 }
464 else {
465 if (_version >= 3.0) {
466 _comments << "Default set of observation types used";
467 _comments.removeDuplicates();
468 _obsTypes['G'] << "C1C" << "L1C" << "S1C"
469 << "C1W" << "L1W" << "S1W"
470 << "C2X" << "L2X" << "S2X"
471 << "C2W" << "L2W" << "S2W"
472 << "C5X" << "L5X" << "S5X";
473
474 _obsTypes['J'] << "C1C" << "L1C" << "S1C"
475 << "C1S" << "L1S" << "S1S"
476 << "C1L" << "L1L" << "S1L"
477 << "C1X" << "L1X" << "S1X"
478 << "C2S" << "L2S" << "S2S"
479 << "C2L" << "L2L" << "S2L"
480 << "C2X" << "L2X" << "S2X"
481 << "C5X" << "L5X" << "S5X";
482
483 _obsTypes['R'] << "C1C" << "L1C" << "S1C"
484 << "C1P" << "L1P" << "S1P"
485 << "C2C" << "L2C" << "S2C"
486 << "C2P" << "L2P" << "S2P";
487
488 _obsTypes['E'] << "C1X" << "L1X" << "S1X"
489 << "C5X" << "L5X" << "S5X"
490 << "C7X" << "L7X" << "S7X"
491 << "C8X" << "L8X" << "S8X";
492
493 _obsTypes['S'] << "C1C" << "L1C" << "S1C"
494 << "C5I" << "L5I" << "S5I"
495 << "C5Q" << "L5Q" << "S5Q";
496
497 _obsTypes['C'] << "C2I" << "L2I" << "S2I"
498 << "C6I" << "L6I" << "S6I"
499 << "C7I" << "L7I" << "S7I";
500
501 _obsTypes['I'] << "C5A" << "L5A" << "S5A"
502 << "C9A" << "L9A" << "S9A";
503 }
504 else {
505 for (int iSys = 0; iSys < header.numSys(); iSys++) {
506 char sys = header.system(iSys);
507 for (int iType = 0; iType < header.nTypes(sys); iType++) {
508 QString type = header.obsType(sys, iType, _version);
509 for (int jSys = 0; jSys < _usedSystems.length(); jSys++) {
510 char thisSys = _usedSystems[jSys].toLatin1();
511 if (!_obsTypes[thisSys].contains(type)) {
512 _obsTypes[thisSys].push_back(type);
513 }
514 }
515 }
516 }
517 }
518 }
519 }
520 else {
521 for (int iType = 0; iType < useObsTypes->size(); iType++) {
522 if (useObsTypes->at(iType).indexOf(":") != -1) {
523 QStringList hlp = useObsTypes->at(iType).split(":", Qt::SkipEmptyParts);
524 if (hlp.size() == 2 && hlp[0].length() == 1) {
525 if (_version >= 3.0) {
526 char sys = hlp[0][0].toLatin1();
527 QString type = t_rnxObsFile::type2to3(sys, hlp[1]);
528 if (!_obsTypes[sys].contains(type)) {
529 _obsTypes[sys].push_back(type);
530 }
531 }
532 else {
533 for (int iSys = 0; iSys < _usedSystems.length(); iSys++) {
534 char sys = _usedSystems[iSys].toLatin1();
535 QString type = t_rnxObsFile::type3to2(sys, hlp[1]);
536 if (!_obsTypes[sys].contains(type)) {
537 _obsTypes[sys].push_back(type);
538 }
539 }
540 }
541 }
542 }
543 else {
544 for (int iSys = 0; iSys < _usedSystems.length(); iSys++) {
545 char sys = _usedSystems[iSys].toLatin1();
546 QString type = _version >= 3.0 ? t_rnxObsFile::type2to3(sys, useObsTypes->at(iType)) :
547 t_rnxObsFile::type3to2(sys, useObsTypes->at(iType));
548 if (!_obsTypes[sys].contains(type)) {
549 _obsTypes[sys].push_back(type);
550 }
551 }
552 }
553 }
554 _usedSystems.clear();
555 QMapIterator<char, QStringList> it(_obsTypes);
556 while (it.hasNext()) {
557 it.next();
558 _usedSystems += QChar(it.key());
559 }
560 }
561
562 if (_version >= 3.0) {
563 // set phase shifts
564 if (!phaseShifts || phaseShifts->empty()) {
565 _phaseShifts = header._phaseShifts;
566 }
567 else {
568 foreach (const QString &str, *phaseShifts) {
569 QStringList hlp = str.split("_", Qt::SkipEmptyParts);
570 QStringList hlp1 = hlp.last().split(":", Qt::SkipEmptyParts);
571 QString type = hlp.first();
572 double shift = hlp1.first().toDouble();
573 hlp1.removeFirst();
574 QString satStr = hlp1.join(" ");
575 if (satStr.size()) {
576 hlp1 = satStr.split(" ");
577 }
578 QStringList &satList = hlp1;
579 QMap<QString, QPair<double, QStringList> >::iterator it = _phaseShifts.find(type);
580 if ( it != _phaseShifts.end()) {
581 it.value().second.append(satList);
582 it.value().second.removeDuplicates();
583 }
584 else {
585 _phaseShifts.insert(type, QPair<double, QStringList>(shift, satList));
586 }
587 }
588 }
589 // set GLONASS biases
590 if (!gloBiases || gloBiases->empty()) {
591 _gloBiases = header._gloBiases;
592 }
593 else {
594 foreach (const QString &str, *gloBiases) {
595 QStringList hlp = str.split(":", Qt::SkipEmptyParts);
596 QString type = hlp.first();;
597 double value = hlp.last().toDouble();
598 if (type.size())
599 _gloBiases[type] = value;
600 }
601 }
602 // set GLONASS slots
603 if (!gloSlots || gloSlots->empty()) {
604 _gloSlots = header._gloSlots;
605 }
606 else {
607 foreach (const QString &str, *gloSlots) {
608 QStringList hlp = str.split(":", Qt::SkipEmptyParts);
609 QString sat = hlp.first();
610 int slot = hlp.last().toInt();
611 t_prn prn;
612 prn.set(sat.toStdString());
613 if(sat.size())
614 _gloSlots[prn] = slot;
615 }
616 }
617 }
618 if (_version >= 4.0) {
619 // set run by date entries
620 if (!runByDate || runByDate->empty()) {
621 _runByDate = header._runByDate;
622 }
623 else {
624 foreach (const QString &str, *runByDate) {
625 _runByDate.append(str);
626 }
627 }
628 }
629
630}
631
632// Write Header
633////////////////////////////////////////////////////////////////////////////
634void t_rnxObsHeader::write(QTextStream* stream,
635 const QMap<QString, QString>* txtMap) const {
636
637 QStringList newComments;
638 QStringList runByDate;
639 QString runBy = BNC_CORE->userName();
640
641 if (txtMap) {
642 QMapIterator<QString, QString> it(*txtMap);
643 while (it.hasNext()) {
644 it.next();
645 if (it.key() == "RUN BY") {
646 runBy = it.value();
647 }
648 else if (it.key() == "RUN BY DATE") {
649 runByDate = it.value().split("\\n", Qt::SkipEmptyParts);
650 }
651 else if ((it.key().indexOf("COMMENT")) != -1) {
652 newComments += it.value().split("\\n", Qt::SkipEmptyParts);
653 }
654 }
655 newComments.removeDuplicates();
656 }
657
658 *stream << QString("%1 OBSERVATION DATA M")
659 .arg(_version, 9, 'f', 2)
660 .leftJustified(60)
661 << "RINEX VERSION / TYPE\n";
662
663 const QString fmtDate = (_version < 3.0) ? "dd-MMM-yy hh:mm"
664 : "yyyyMMdd hhmmss UTC";
665 *stream << QString("%1%2%3")
666 .arg(BNC_CORE->pgmName(), -20)
667 .arg(runBy.trimmed().left(20), -20)
668 .arg(QDateTime::currentDateTime().toUTC().toString(fmtDate), -20)
669 .leftJustified(60)
670 << "PGM / RUN BY / DATE\n";
671
672 if (_version >= 4.0) {
673 QStringListIterator itRunByDt(_runByDate);
674 while (itRunByDt.hasNext()) {
675 *stream << itRunByDt.next().trimmed().left(60).leftJustified(60)
676 << "PGM / RUN BY / DATE\n";
677 }
678 }
679 QStringListIterator itCmnt(_comments + newComments);
680 while (itCmnt.hasNext()) {
681 *stream << itCmnt.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
682 }
683 QString markerName = _markerName.left(9);
684 if (_version < 3.0) {
685 markerName = _markerName.left(4);
686 }
687 *stream << QString("%1")
688 .arg(markerName, -60)
689 .leftJustified(60)
690 << "MARKER NAME\n";
691
692
693 if (!_markerNumber.isEmpty()) {
694 *stream << QString("%1")
695 .arg(_markerNumber, -20)
696 .leftJustified(60)
697 << "MARKER NUMBER\n";
698 }
699
700 if (_version >= 3.0) {
701 *stream << QString("%1")
702 .arg(_markerType, -60)
703 .leftJustified(60)
704 << "MARKER TYPE\n";
705 }
706
707 *stream << QString("%1%2")
708 .arg(_observer, -20)
709 .arg(_agency, -40)
710 .leftJustified(60)
711 << "OBSERVER / AGENCY\n";
712
713 *stream << QString("%1%2%3")
714 .arg(_receiverNumber, -20)
715 .arg(_receiverType, -20)
716 .arg(_receiverVersion, -20)
717 .leftJustified(60)
718 << "REC # / TYPE / VERS\n";
719
720 *stream << QString("%1%2")
721 .arg(_antennaNumber, -20)
722 .arg(_antennaName, -20)
723 .leftJustified(60)
724 << "ANT # / TYPE\n";
725
726 *stream << QString("%1%2%3")
727 .arg(_xyz(1), 14, 'f', 4)
728 .arg(_xyz(2), 14, 'f', 4)
729 .arg(_xyz(3), 14, 'f', 4)
730 .leftJustified(60)
731 << "APPROX POSITION XYZ\n";
732
733 *stream << QString("%1%2%3")
734 .arg(_antNEU(3), 14, 'f', 4)
735 .arg(_antNEU(2), 14, 'f', 4)
736 .arg(_antNEU(1), 14, 'f', 4)
737 .leftJustified(60)
738 << "ANTENNA: DELTA H/E/N\n";
739
740 if (_version < 3.0) {
741 int defaultWlFact1 = _wlFactorsL1[1];
742 int defaultWlFact2 = _wlFactorsL2[1]; // TODO check all prns
743 *stream << QString("%1%2")
744 .arg(defaultWlFact1, 6)
745 .arg(defaultWlFact2, 6)
746 .leftJustified(60)
747 << "WAVELENGTH FACT L1/2\n";
748 }
749
750 *stream << obsTypesStrings().join("");
751
752 if (_interval > 0) {
753 *stream << QString("%1")
754 .arg(_interval, 10, 'f', 3)
755 .leftJustified(60)
756 << "INTERVAL\n";
757 }
758
759 unsigned year, month, day, hour, min;
760 double sec;
761 _startTime.civil_date(year, month, day);
762 _startTime.civil_time(hour, min, sec);
763 *stream << QString("%1%2%3%4%5%6%7")
764 .arg(year, 6)
765 .arg(month, 6)
766 .arg(day, 6)
767 .arg(hour, 6)
768 .arg(min, 6)
769 .arg(sec, 13, 'f', 7)
770 .arg("GPS", 8)
771 .leftJustified(60)
772 << "TIME OF FIRST OBS\n";
773
774 if (_version >= 3.0) {
775 if (_phaseShifts.empty()) {
776 QMap<char, QStringList>::const_iterator it;
777 for (it = _obsTypes.begin(); it != _obsTypes.end(); ++it) {
778 char sys = it.key();
779 double shift = 0.0;
780 foreach (const QString &obstype, it.value()) {
781 if (obstype.left(1).contains('L')) {
782 *stream << QString("%1%2%3")
783 .arg(sys, 0)
784 .arg(obstype, 4)
785 .arg(shift, 9, 'f', 5)
786 .leftJustified(60)
787 << "SYS / PHASE SHIFT\n";
788 }
789 }
790 }
791 } else {
792 QMap<QString, QPair<double, QStringList> >::const_iterator it;
793 QString emptyFillStr;
794 for(it = _phaseShifts.begin(); it!= _phaseShifts.end(); ++it) {
795 QString sys = it.key().left(1);
796 QString obstype = it.key().mid(1);
797 double shift = it.value().first;
798 QStringList satList = it.value().second;
799 satList.sort();
800 QString hlp;
801 if (obstype.isEmpty()) {
802 hlp = QString("%1")
803 .arg(sys.toStdString().c_str(), 0);
804 }
805 else {
806 hlp = QString("%1%2")
807 .arg(sys.toStdString().c_str(), 0)
808 .arg(obstype, 4);
809 }
810 if (shift) {
811 hlp += QString("%1")
812 .arg(shift, 9, 'f', 5);
813 }
814 if (!satList.empty()) {
815 hlp += QString("%1").arg(satList.size(), 4);
816 }
817 else {
818 *stream << QString("%1")
819 .arg(hlp, 0)
820 .leftJustified(60)
821 << "SYS / PHASE SHIFT\n";
822 hlp = "";
823 }
824 int ii = 0;
825 QStringList::const_iterator it_s;
826 for (it_s = satList.begin(); it_s != satList.end(); ++it_s) {
827 (hlp.contains(obstype)) ?
828 emptyFillStr = "": emptyFillStr = " ";
829 hlp += QString("%1").arg(*it_s, 4);
830 ii++;
831 if (ii % 10 == 0) {
832 *stream << QString("%1%2")
833 .arg(emptyFillStr, 0)
834 .arg(hlp, 0)
835 .leftJustified(60)
836 << "SYS / PHASE SHIFT\n";
837 hlp = "";
838 }
839 }
840 if (hlp.size()) {
841 (hlp.contains(obstype)) ?
842 emptyFillStr = "": emptyFillStr = " ";
843 *stream << QString("%1%2")
844 .arg(emptyFillStr, 0)
845 .arg(hlp, 0)
846 .leftJustified(60)
847 << "SYS / PHASE SHIFT\n";
848 }
849 }
850 }
851 }
852
853 if (_version >= 3.0) {
854 QString hlp = "";
855 QMap<QString, double>::const_iterator it = _gloBiases.begin();
856 while (it != _gloBiases.end()){
857 hlp += QString("%1%2").arg(it.key(), 4).arg(it.value(), 9, 'f', 3);
858 it++;
859 }
860 *stream << QString("%1")
861 .arg(hlp, 0)
862 .leftJustified(60)
863 << "GLONASS COD/PHS/BIS\n";
864 }
865
866 if (_version >= 3.0) {
867 QString number = QString::number(_gloSlots.size());
868 QString hlp = "";
869 int ii = 0;
870 QMap<t_prn, int>::const_iterator it = _gloSlots.begin();
871 while (it != _gloSlots.end()) {
872 QString prn(it.key().toString().c_str());
873 hlp += QString("%1%2").arg(prn, 4).arg(it.value(), 3);
874 it++;
875 ii++;
876 if (ii % 8 == 0) {
877 *stream << QString("%1%2")
878 .arg(number, 3)
879 .arg(hlp, 0)
880 .leftJustified(60)
881 << "GLONASS SLOT / FRQ #\n";
882 ii = 0;
883 hlp = number = "";
884 }
885 }
886 if (hlp.size() || !_gloSlots.size()) {
887 *stream << QString("%1%2")
888 .arg(number, 3)
889 .arg(hlp, 0)
890 .leftJustified(60)
891 << "GLONASS SLOT / FRQ #\n";
892 }
893 }
894
895 *stream << QString()
896 .leftJustified(60)
897 << "END OF HEADER\n";
898}
899
900// Number of Different Systems
901////////////////////////////////////////////////////////////////////////////
902int t_rnxObsHeader::numSys() const {
903 return _obsTypes.size();
904}
905
906//
907////////////////////////////////////////////////////////////////////////////
908char t_rnxObsHeader::system(int iSys) const {
909 int iSysLocal = -1;
910 QMapIterator<char, QStringList> it(_obsTypes);
911 while (it.hasNext()) {
912 ++iSysLocal;
913 it.next();
914 if (iSysLocal == iSys) {
915 return it.key();
916 }
917 }
918 return ' ';
919}
920
921//
922////////////////////////////////////////////////////////////////////////////
923QString t_rnxObsHeader::usedSystems(void) const {
924 return _usedSystems;
925}
926
927//
928////////////////////////////////////////////////////////////////////////////
929QStringList t_rnxObsHeader::obsTypes(char sys) const {
930 if (_obsTypes.contains(sys)) {
931 return _obsTypes[sys];
932 }
933 else {
934 return QStringList();
935 }
936}
937
938// Number of Observation Types (satellite-system specific)
939////////////////////////////////////////////////////////////////////////////
940int t_rnxObsHeader::nTypes(char sys) const {
941 if (_obsTypes.contains(sys)) {
942 return _obsTypes[sys].size();
943 }
944 else {
945 return 0;
946 }
947}
948
949// Number of GLONASS biases
950////////////////////////////////////////////////////////////////////////////
951int t_rnxObsHeader::numGloBiases() const {
952 return _gloBiases.size();
953}
954
955// Number of GLONASS slots
956////////////////////////////////////////////////////////////////////////////
957int t_rnxObsHeader::numGloSlots() const {
958 return _gloSlots.size();
959}
960
961// Observation Type (satellite-system specific)
962////////////////////////////////////////////////////////////////////////////
963QString t_rnxObsHeader::obsType(char sys, int index, double version) const {
964 if (version == 0.0) {
965 version = _version;
966 }
967
968 if (_obsTypes.contains(sys)) {
969 QString origType = _obsTypes[sys].at(index);
970 if (int(version) == int(_version)) {
971 return origType;
972 }
973 else if (int(version) == 2) {
974 return t_rnxObsFile::type3to2(sys, origType);
975 }
976 else {
977 return t_rnxObsFile::type2to3(sys, origType);
978 }
979 }
980 return "";
981}
982
983//
984////////////////////////////////////////////////////////////////////////////
985QStringList t_rnxObsHeader::phaseShifts() const {
986 QStringList strList;
987 QMap<QString, QPair<double, QStringList> >::const_iterator it = _phaseShifts.begin();
988 while (it != _phaseShifts.end()) {
989 strList.append(QString("%1_%2:%3").arg(it.key(), 3).arg(it.value().first, 9, 'f', 3).arg(it.value().second.join(" ")));
990 it++;
991 }
992 return strList;
993}
994
995//
996////////////////////////////////////////////////////////////////////////////
997QStringList t_rnxObsHeader::gloBiases() const {
998 QStringList strList;
999 QMap<QString, double>::const_iterator it = _gloBiases.begin();
1000 while (it != _gloBiases.end()) {
1001 strList.append(QString("%1:%2").arg(it.key(), 3).arg(it.value(), 9, 'f', 3));
1002 it++;
1003 }
1004 return strList;
1005}
1006
1007//
1008////////////////////////////////////////////////////////////////////////////
1009QStringList t_rnxObsHeader::gloSlots() const {
1010 QStringList strList;
1011 QMap<t_prn, int>::const_iterator it = _gloSlots.begin();
1012 while (it != _gloSlots.end()){
1013 QString prn(it.key().toString().c_str());
1014 strList.append(QString("%1:%2").arg(prn, 3).arg(it.value()));
1015 it++;
1016 }
1017 return strList;
1018}
1019
1020// Write Observation Types
1021////////////////////////////////////////////////////////////////////////////
1022QStringList t_rnxObsHeader::obsTypesStrings() const {
1023
1024 QStringList strList;
1025 if (_version < 3.0) {
1026 char sys0 = _usedSystems[0].toLatin1();
1027 QString hlp;
1028 QTextStream(&hlp) << QString("%1").arg(_obsTypes[sys0].size(), 6);
1029 for (int ii = 0; ii < _obsTypes[sys0].size(); ii++) {
1030 QTextStream(&hlp) << QString("%1").arg(_obsTypes[sys0][ii], 6);
1031 if ((ii+1) % 9 == 0 || ii == _obsTypes[sys0].size()-1) {
1032 strList.append(hlp.leftJustified(60) + "# / TYPES OF OBSERV\n");
1033 hlp = QString().leftJustified(6);
1034 }
1035 }
1036 }
1037 else {
1038 for (int iSys = 0; iSys < numSys(); iSys++) {
1039 char sys = system(iSys);
1040 QString hlp;
1041 QTextStream(&hlp) << QString("%1 %2").arg(sys).arg(nTypes(sys), 3);
1042 for (int iType = 0; iType < nTypes(sys); iType++) {
1043 QString type = obsType(sys, iType);
1044 QTextStream(&hlp) << QString(" %1").arg(type, -3);
1045 if ((iType+1) % 13 == 0 || iType == nTypes(sys)-1) {
1046 strList.append(hlp.leftJustified(60) + "SYS / # / OBS TYPES\n");
1047 hlp = QString().leftJustified(6);
1048 }
1049 }
1050 }
1051 }
1052
1053 return strList;
1054}
1055
1056// Constructor
1057////////////////////////////////////////////////////////////////////////////
1058t_rnxObsFile::t_rnxObsFile(const QString& fileName, e_inpOut inpOut) {
1059 _inpOut = inpOut;
1060 _stream = 0;
1061 _flgPowerFail = false;
1062 if (_inpOut == input) {
1063 openRead(fileName);
1064 }
1065 else {
1066 openWrite(fileName);
1067 }
1068}
1069
1070// Open for input
1071////////////////////////////////////////////////////////////////////////////
1072void t_rnxObsFile::openRead(const QString& fileName) {
1073
1074 _fileName = fileName; expandEnvVar(_fileName);
1075 _file = new QFile(_fileName);
1076 _file->open(QIODevice::ReadOnly | QIODevice::Text);
1077 _stream = new QTextStream();
1078 _stream->setDevice(_file);
1079
1080 _header.read(_stream);
1081
1082 // Guess Observation Interval
1083 // --------------------------
1084 if (_header._interval == 0.0) {
1085 bncTime ttPrev;
1086 for (int iEpo = 0; iEpo < 10; iEpo++) {
1087 const t_rnxEpo* rnxEpo = nextEpoch();
1088 if (!rnxEpo) {
1089 throw QString("t_rnxObsFile: not enough epochs");
1090 }
1091 if (iEpo > 0) {
1092 double dt = rnxEpo->tt - ttPrev;
1093 if (_header._interval == 0.0 || dt < _header._interval) {
1094 _header._interval = dt;
1095 }
1096 }
1097 ttPrev = rnxEpo->tt;
1098 }
1099 _stream->seek(0);
1100 _header.read(_stream);
1101 }
1102
1103 // Time of first observation
1104 // -------------------------
1105 if (!_header._startTime.valid()) {
1106 const t_rnxEpo* rnxEpo = nextEpoch();
1107 if (!rnxEpo) {
1108 throw QString("t_rnxObsFile: not enough epochs");
1109 }
1110 _header._startTime = rnxEpo->tt;
1111 _stream->seek(0);
1112 _header.read(_stream);
1113 }
1114}
1115
1116// Open for output
1117////////////////////////////////////////////////////////////////////////////
1118void t_rnxObsFile::openWrite(const QString& fileName) {
1119
1120 _fileName = fileName; expandEnvVar(_fileName);
1121 _file = new QFile(_fileName);
1122 _file->open(QIODevice::WriteOnly | QIODevice::Text);
1123 _stream = new QTextStream();
1124 _stream->setDevice(_file);
1125}
1126
1127// Destructor
1128////////////////////////////////////////////////////////////////////////////
1129t_rnxObsFile::~t_rnxObsFile() {
1130 close();
1131}
1132
1133// Close
1134////////////////////////////////////////////////////////////////////////////
1135void t_rnxObsFile::close() {
1136 delete _stream; _stream = 0;
1137 delete _file; _file = 0;
1138}
1139
1140// Handle Special Epoch Flag
1141////////////////////////////////////////////////////////////////////////////
1142void t_rnxObsFile::handleEpochFlag(int flag, const QString& line,
1143 bool& headerReRead) {
1144
1145 headerReRead = false;
1146
1147 // Power Failure
1148 // -------------
1149 if (flag == 1) {
1150 _flgPowerFail = true;
1151 }
1152
1153 // Start moving antenna
1154 // --------------------
1155 else if (flag == 2) {
1156 // no action
1157 }
1158
1159 // Re-Read Header
1160 // --------------
1161 else if (flag == 3 || flag == 4 || flag == 5) {
1162 int numLines = 0;
1163 if (version() < 3.0) {
1164 readInt(line, 29, 3, numLines);
1165 }
1166 else {
1167 readInt(line, 32, 3, numLines);
1168 }
1169 if (flag == 3 || flag == 4) {
1170 _header.read(_stream, numLines);
1171 headerReRead = true;
1172 }
1173 else {
1174 for (int ii = 0; ii < numLines; ii++) {
1175 _stream->readLine();
1176 }
1177 }
1178 }
1179
1180 // Unhandled Flag
1181 // --------------
1182 else {
1183 throw QString("t_rnxObsFile: unhandled flag\n" + line);
1184 }
1185}
1186
1187// Retrieve single Epoch
1188////////////////////////////////////////////////////////////////////////////
1189t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpoch() {
1190 _currEpo.clear();
1191 if (version() < 3.0) {
1192 return nextEpochV2();
1193 }
1194 else {
1195 return nextEpochV3();
1196 }
1197}
1198
1199// Retrieve single Epoch (RINEX Version 3)
1200////////////////////////////////////////////////////////////////////////////
1201t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV3() {
1202
1203 while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
1204
1205 QString line = _stream->readLine();
1206
1207 if (line.isEmpty()) {
1208 continue;
1209 }
1210
1211 int flag = 0;
1212 readInt(line, 31, 1, flag);
1213 if (flag > 0) {
1214 bool headerReRead = false;
1215 handleEpochFlag(flag, line, headerReRead);
1216 if (headerReRead) {
1217 continue;
1218 }
1219 }
1220
1221 QTextStream in(line.mid(1).toLatin1(), QIODevice::ReadOnly);
1222
1223 // Epoch Time
1224 // ----------
1225 int year, month, day, hour, min;
1226 double sec;
1227 in >> year >> month >> day >> hour >> min >> sec;
1228 _currEpo.tt.set(year, month, day, hour, min, sec);
1229
1230 // Number of Satellites
1231 // --------------------
1232 int numSat;
1233 readInt(line, 32, 3, numSat);
1234
1235 _currEpo.rnxSat.resize(numSat);
1236
1237 // Observations
1238 // ------------
1239 for (int iSat = 0; iSat < numSat; iSat++) {
1240 line = _stream->readLine();
1241 t_prn prn; prn.set(line.left(3).toLatin1().data());
1242 char sys = prn.system();
1243 int num = prn.number();
1244 int flag = t_corrSSR::getSsrNavTypeFlag(sys, num);
1245 prn.setFlag(flag);
1246 _currEpo.rnxSat[iSat].prn = prn;
1247 for (int iType = 0; iType < _header.nTypes(sys); iType++) {
1248 int pos = 3 + 16*iType;
1249 double obsValue = 0.0;
1250 int lli = 0;
1251 int snr = 0;
1252 readDbl(line, pos, 14, obsValue);
1253 readInt(line, pos + 14, 1, lli);
1254 readInt(line, pos + 15, 1, snr);
1255 if (_flgPowerFail) {
1256 lli |= 1;
1257 }
1258 QString type = obsType(sys, iType);
1259 _currEpo.rnxSat[iSat].obs[type].value = obsValue;
1260 _currEpo.rnxSat[iSat].obs[type].lli = lli;
1261 _currEpo.rnxSat[iSat].obs[type].snr = snr;
1262 }
1263 }
1264
1265 _flgPowerFail = false;
1266
1267 return &_currEpo;
1268 }
1269
1270 return 0;
1271}
1272
1273// Retrieve single Epoch (RINEX Version 2)
1274////////////////////////////////////////////////////////////////////////////
1275t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV2() {
1276
1277 while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
1278
1279 QString line = _stream->readLine();
1280
1281 if (line.isEmpty()) {
1282 continue;
1283 }
1284
1285 int flag = 0;
1286 readInt(line, 28, 1, flag);
1287 if (flag > 0) {
1288 bool headerReRead = false;
1289 handleEpochFlag(flag, line, headerReRead);
1290 if (headerReRead) {
1291 continue;
1292 }
1293 }
1294
1295 QTextStream in(line.toLatin1(), QIODevice::ReadOnly);
1296
1297 // Epoch Time
1298 // ----------
1299 int year, month, day, hour, min;
1300 double sec;
1301 in >> year >> month >> day >> hour >> min >> sec;
1302 if (year < 80) {
1303 year += 2000;
1304 }
1305 else if (year < 100) {
1306 year += 1900;
1307 }
1308 _currEpo.tt.set(year, month, day, hour, min, sec);
1309
1310 // Number of Satellites
1311 // --------------------
1312 int numSat;
1313 readInt(line, 29, 3, numSat);
1314
1315 _currEpo.rnxSat.resize(numSat);
1316
1317 // Read Satellite Numbers
1318 // ----------------------
1319 int pos = 32;
1320 for (int iSat = 0; iSat < numSat; iSat++) {
1321 if (iSat > 0 && iSat % 12 == 0) {
1322 line = _stream->readLine();
1323 pos = 32;
1324 }
1325
1326 char sys = line.toLatin1()[pos];
1327 if (sys == ' ') {
1328 sys = 'G';
1329 }
1330 int num; readInt(line, pos + 1, 2, num);
1331 int flag = t_corrSSR::getSsrNavTypeFlag(sys, num);
1332 t_prn prn;
1333 prn.set(sys, num, flag);
1334 _currEpo.rnxSat[iSat].prn = prn;
1335
1336 pos += 3;
1337 }
1338
1339 // Read Observation Records
1340 // ------------------------
1341 for (int iSat = 0; iSat < numSat; iSat++) {
1342 char sys = _currEpo.rnxSat[iSat].prn.system();
1343 line = _stream->readLine();
1344 pos = 0;
1345 for (int iType = 0; iType < _header.nTypes(sys); iType++) {
1346 if (iType > 0 && iType % 5 == 0) {
1347 line = _stream->readLine();
1348 pos = 0;
1349 }
1350 double obsValue = 0.0;
1351 int lli = 0;
1352 int snr = 0;
1353 readDbl(line, pos, 14, obsValue);
1354 readInt(line, pos + 14, 1, lli);
1355 readInt(line, pos + 15, 1, snr);
1356
1357 if (_flgPowerFail) {
1358 lli |= 1;
1359 }
1360
1361 QString type = obsType(sys, iType);
1362 _currEpo.rnxSat[iSat].obs[type].value = obsValue;
1363 _currEpo.rnxSat[iSat].obs[type].lli = lli;
1364 _currEpo.rnxSat[iSat].obs[type].snr = snr;
1365
1366 pos += 16;
1367 }
1368 }
1369
1370 _flgPowerFail = false;
1371
1372 return &_currEpo;
1373 }
1374
1375 return 0;
1376}
1377
1378// Write Data Epoch
1379////////////////////////////////////////////////////////////////////////////
1380void t_rnxObsFile::writeEpoch(const t_rnxEpo* epo) {
1381 if (epo == 0) {
1382 return;
1383 }
1384 t_rnxEpo epoLocal;
1385 epoLocal.tt = epo->tt;
1386 for (unsigned ii = 0; ii < epo->rnxSat.size(); ii++) {
1387 const t_rnxSat& rnxSat = epo->rnxSat[ii];
1388 if (_header._obsTypes[rnxSat.prn.system()].size() > 0) {
1389 if (_header.version() < 3.0) { // exclude new GNSS such as BDS, QZSS, NavIC, etc.
1390 if (rnxSat.prn.system() != 'G' && rnxSat.prn.system() != 'R' &&
1391 rnxSat.prn.system() != 'E' && rnxSat.prn.system() != 'S' &&
1392 rnxSat.prn.system() != 'I') {
1393 continue;
1394 }
1395 }
1396 epoLocal.rnxSat.push_back(rnxSat);
1397 }
1398 }
1399 std::stable_sort(epoLocal.rnxSat.begin(), epoLocal.rnxSat.end(), t_rnxSat::prnSort);
1400
1401 if (version() < 3.0) {
1402 return writeEpochV2(_stream, _header, &epoLocal);
1403 }
1404 else {
1405 return writeEpochV3(_stream, _header, &epoLocal);
1406 }
1407}
1408
1409// Write Data Epoch (RINEX Version 2)
1410////////////////////////////////////////////////////////////////////////////
1411void t_rnxObsFile::writeEpochV2(QTextStream* stream, const t_rnxObsHeader& header,
1412 const t_rnxEpo* epo) {
1413
1414 unsigned year, month, day, hour, min;
1415 double sec;
1416 epo->tt.civil_date(year, month, day);
1417 epo->tt.civil_time(hour, min, sec);
1418
1419 QString dateStr;
1420 QTextStream(&dateStr) << QString(" %1 %2 %3 %4 %5%6")
1421 .arg(int(fmod(double(year), 100.0)), 2, 10, QChar('0'))
1422 .arg(month, 2, 10, QChar('0'))
1423 .arg(day, 2, 10, QChar('0'))
1424 .arg(hour, 2, 10, QChar('0'))
1425 .arg(min, 2, 10, QChar('0'))
1426 .arg(sec, 11, 'f', 7);
1427
1428 int flag = 0;
1429 *stream << dateStr << QString("%1%2").arg(flag, 3).arg(epo->rnxSat.size(), 3);
1430 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1431 const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1432 if (iSat > 0 && iSat % 12 == 0) {
1433 *stream << Qt::endl << QString().leftJustified(32);
1434 }
1435 *stream << rnxSat.prn.toString().c_str();
1436 }
1437 *stream << Qt::endl;
1438
1439 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1440 const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1441 char sys = rnxSat.prn.system();
1442 for (int iTypeV2 = 0; iTypeV2 < header.nTypes(sys); iTypeV2++) {
1443 if (iTypeV2 > 0 && iTypeV2 % 5 == 0) {
1444 *stream << Qt::endl;
1445 }
1446 QString typeV2 = header.obsType(sys, iTypeV2);
1447 bool found = false;
1448 QStringList preferredAttribList = signalPriorities(sys);
1449 QString preferredAttrib;
1450 for (int ii = 0; ii < preferredAttribList.size(); ii++) {
1451 if (preferredAttribList[ii].indexOf("&") != -1) {
1452 QStringList hlp = preferredAttribList[ii].split("&", Qt::SkipEmptyParts);
1453 if (hlp.size() == 2 && hlp[0].contains(typeV2[1])) {
1454 preferredAttrib = hlp[1];
1455 }
1456 }
1457 else {
1458 preferredAttrib = preferredAttribList[ii];
1459 }
1460 }
1461
1462 for (int iPref = 0; iPref < preferredAttrib.size(); iPref++) {
1463 QMapIterator<QString, t_rnxObs> itObs(rnxSat.obs);
1464 while (itObs.hasNext()) {
1465 itObs.next();
1466 const QString& type = itObs.key();
1467 const t_rnxObs& rnxObs = itObs.value();
1468 if ( preferredAttrib[iPref] == '?' ||
1469 (type.length() == 2 && preferredAttrib[iPref] == '_' ) ||
1470 (type.length() == 3 && preferredAttrib[iPref] == type[2]) ) {
1471 if (typeV2 == type3to2(sys, type)) {
1472 found = true;
1473 if (rnxObs.value == 0.0) {
1474 *stream << QString().leftJustified(16);
1475 }
1476 else {
1477 *stream << QString("%1").arg(rnxObs.value, 14, 'f', 3);
1478 if (rnxObs.lli != 0.0) {
1479 *stream << QString("%1").arg(rnxObs.lli,1);
1480 }
1481 else {
1482 *stream << ' ';
1483 }
1484 if (rnxObs.snr != 0.0) {
1485 *stream << QString("%1").arg(rnxObs.snr,1);
1486 }
1487 else {
1488 *stream << ' ';
1489 }
1490 }
1491 goto end_loop_iPref;
1492 }
1493 }
1494 }
1495 } end_loop_iPref:
1496 if (!found) {
1497 *stream << QString().leftJustified(16);
1498 }
1499 }
1500 *stream << Qt::endl;
1501 }
1502}
1503
1504// Write Data Epoch (RINEX Version 3)
1505////////////////////////////////////////////////////////////////////////////
1506void t_rnxObsFile::writeEpochV3(QTextStream* stream, const t_rnxObsHeader& header,
1507 const t_rnxEpo* epo) {
1508
1509 unsigned year, month, day, hour, min;
1510 double sec;
1511 epo->tt.civil_date(year, month, day);
1512 epo->tt.civil_time(hour, min, sec);
1513
1514 QString dateStr;
1515 QTextStream(&dateStr) << QString("> %1 %2 %3 %4 %5%6")
1516 .arg(year, 4)
1517 .arg(month, 2, 10, QChar('0'))
1518 .arg(day, 2, 10, QChar('0'))
1519 .arg(hour, 2, 10, QChar('0'))
1520 .arg(min, 2, 10, QChar('0'))
1521 .arg(sec, 11, 'f', 7);
1522
1523 int flag = 0;
1524 *stream << dateStr << QString("%1%2\n").arg(flag, 3).arg(epo->rnxSat.size(), 3);
1525
1526 for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1527 const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1528 char sys = rnxSat.prn.system();
1529
1530 std::vector <const t_rnxObs*> hlp(header.nTypes(sys));
1531 for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
1532 hlp[iTypeV3] = 0;
1533 QString typeV3 = header.obsType(sys, iTypeV3);
1534 QMapIterator<QString, t_rnxObs> itObs(rnxSat.obs);
1535
1536 // Exact match
1537 // -----------
1538 while (itObs.hasNext()) {
1539 itObs.next();
1540 const QString& type = itObs.key();
1541 const t_rnxObs& rnxObs = itObs.value();
1542 if (typeV3 == type2to3(sys, type) && rnxObs.value != 0.0) {
1543 hlp[iTypeV3] = &itObs.value();
1544 }
1545 }
1546
1547 // Non-Exact match
1548 // ---------------
1549 itObs.toFront();
1550 while (itObs.hasNext()) {
1551 itObs.next();
1552 const QString& type = itObs.key();
1553 const t_rnxObs& rnxObs = itObs.value();
1554 if (hlp[iTypeV3] == 0 && typeV3 == type2to3(sys, type).left(2) && rnxObs.value != 0.0) {
1555 hlp[iTypeV3] = &itObs.value();
1556 }
1557 }
1558 }
1559
1560 if (header.nTypes(sys)) {
1561 *stream << rnxSat.prn.toString().c_str();
1562 for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
1563 const t_rnxObs* rnxObs = hlp[iTypeV3];
1564 if (rnxObs == 0) {
1565 *stream << QString().leftJustified(16);
1566 }
1567 else {
1568 *stream << QString("%1").arg(rnxObs->value, 14, 'f', 3);
1569 if (rnxObs->lli != 0.0) {
1570 *stream << QString("%1").arg(rnxObs->lli, 1);
1571 }
1572 else {
1573 *stream << ' ';
1574 }
1575 if (rnxObs->snr != 0.0) {
1576 *stream << QString("%1").arg(rnxObs->snr, 1);
1577 }
1578 else {
1579 *stream << ' ';
1580 }
1581 }
1582 }
1583 *stream << Qt::endl;
1584 }
1585 }
1586}
1587
1588// Translate Observation Type v2 --> v3
1589////////////////////////////////////////////////////////////////////////////
1590QString t_rnxObsFile::type2to3(char sys, const QString& typeV2) {
1591 if (typeV2 == "P1") {
1592 return (sys == 'G') ? "C1W" : "C1P";
1593 }
1594 else if (typeV2 == "P2") {
1595 return (sys == 'G') ? "C2W" : "C2P";
1596 }
1597 return typeV2;
1598}
1599
1600// Translate Observation Type v3 --> v2
1601////////////////////////////////////////////////////////////////////////////
1602QString t_rnxObsFile::type3to2(char /* sys */, const QString& typeV3) {
1603 if (typeV3 == "C1P" || typeV3 == "C1W") {
1604 return "P1";
1605 }
1606 else if (typeV3 == "C2P" || typeV3 == "C2W") {
1607 return "P2";
1608 }
1609 return typeV3.left(2);
1610}
1611
1612// Set Observations from RINEX File
1613////////////////////////////////////////////////////////////////////////////
1614void t_rnxObsFile::setObsFromRnx(const t_rnxObsFile* rnxObsFile, const t_rnxObsFile::t_rnxEpo* epo,
1615 const t_rnxObsFile::t_rnxSat& rnxSat, t_satObs& obs) {
1616 obs._staID = rnxObsFile->markerName().toLatin1().constData();
1617 obs._time = epo->tt;
1618 obs._prn = rnxSat.prn;
1619
1620 char sys = obs._prn.system();
1621 int num = obs._prn.number();
1622 int flag = t_corrSSR::getSsrNavTypeFlag(sys, num);
1623 obs._prn.setFlag(flag);
1624
1625 QChar addToL2;
1626 for (int iType = 0; iType < rnxObsFile->nTypes(sys); iType++) {
1627 QString type = rnxObsFile->obsType(sys, iType);
1628 QString typeV3 = rnxObsFile->obsType(sys, iType, 3.0); // may or may not differ from type
1629 if (rnxSat.obs.contains(type) && rnxSat.obs[type].value != 0.0) {
1630 if (type == "P2" && typeV3.length() > 2) {
1631 addToL2 = typeV3[2];
1632 break;
1633 }
1634 }
1635 }
1636
1637 for (int iType = 0; iType < rnxObsFile->nTypes(sys); iType++) {
1638 QString type = rnxObsFile->obsType(sys, iType);
1639 QString typeV3 = rnxObsFile->obsType(sys, iType, 3.0); // may or may not differ from type
1640 if (type == "L2") {
1641 typeV3 += addToL2;
1642 }
1643 if (rnxSat.obs.contains(type)) {
1644 const t_rnxObs& rnxObs = rnxSat.obs[type];
1645 if (rnxObs.value != 0.0) {
1646 string type2ch(typeV3.mid(1).toLatin1().data());
1647
1648 t_frqObs* frqObs = 0;
1649 for (unsigned iFrq = 0; iFrq < obs._obs.size(); iFrq++) {
1650 if (obs._obs[iFrq]->_rnxType2ch == type2ch) {
1651 frqObs = obs._obs[iFrq];
1652 break;
1653 }
1654 }
1655 if (frqObs == 0) {
1656 frqObs = new t_frqObs;
1657 frqObs->_rnxType2ch = type2ch;
1658 obs._obs.push_back(frqObs);
1659 }
1660
1661 switch( typeV3.toLatin1().data()[0] ) {
1662 case 'C':
1663 frqObs->_codeValid = true;
1664 frqObs->_code = rnxObs.value;
1665 break;
1666 case 'L':
1667 frqObs->_phaseValid = true;
1668 frqObs->_phase = rnxObs.value;
1669 frqObs->_slip = (rnxObs.lli & 1);
1670 break;
1671 case 'D':
1672 frqObs->_dopplerValid = true;
1673 frqObs->_doppler = rnxObs.value;
1674 break;
1675 case 'S':
1676 frqObs->_snrValid = true;
1677 frqObs->_snr = rnxObs.value;
1678 break;
1679 }
1680
1681 // Handle old-fashioned SNR values
1682 // -------------------------------
1683 if (rnxObs.snr != 0 && !frqObs->_snrValid) {
1684 frqObs->_snrValid = true;
1685 frqObs->_snr = rnxObs.snr * 6.0 + 2.5;
1686 }
1687 }
1688 }
1689 }
1690}
1691
1692// Tracking Mode Priorities
1693////////////////////////////////////////////////////////////////////////////
1694QStringList t_rnxObsFile::signalPriorities(char sys) {
1695
1696 bncSettings settings;
1697
1698 QStringList priorList;
1699 QString reqcAction = settings.value("reqcAction").toString();
1700
1701 // Priorities in Edit/Concatenate (post processing) mode
1702 // ---------------------------------------------------
1703 if (reqcAction == "Edit/Concatenate") {
1704 priorList = settings.value("reqcV2Priority").toString().split(" ", Qt::SkipEmptyParts);
1705 }
1706
1707 // Priorities in real-time mode
1708 // ----------------------------
1709 else {
1710 priorList = settings.value("rnxV2Priority").toString().split(" ", Qt::SkipEmptyParts);
1711 }
1712
1713 QStringList result;
1714 for (int ii = 0; ii < priorList.size(); ii++) {
1715 if (priorList[ii].indexOf(":") != -1) {
1716 QStringList hlp = priorList[ii].split(":", Qt::SkipEmptyParts);
1717 if (hlp.size() == 2 && hlp[0].length() == 1 && hlp[0][0] == sys) {
1718 result.append(hlp[1]);
1719 }
1720 }
1721 else {
1722 result.append(priorList[ii]);
1723 }
1724 }
1725
1726 if (result.empty()) {
1727 switch (sys) {
1728 case 'G':
1729 result.append("12&PWCSLXYN");
1730 result.append("5&IQX");
1731 break;
1732 case 'R':
1733 result.append("12&PC");
1734 result.append("3&IQX");
1735 break;
1736 case 'E':
1737 result.append("16&BCX");
1738 result.append("578&IQX");
1739 break;
1740 case 'J':
1741 result.append("1&SLXCZ");
1742 result.append("26&SLX");
1743 result.append("5&IQX");
1744 break;
1745 case 'C':
1746 result.append("IQX");
1747 break;
1748 case 'I':
1749 result.append("ABCX");
1750 break;
1751 case 'S':
1752 result.append("1&C");
1753 result.append("5&IQX");
1754 break;
1755 }
1756 }
1757 return result;
1758}
Note: See TracBrowser for help on using the repository browser.