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

Last change on this file since 9760 was 9760, checked in by stuerze, 2 months ago

initial changes to consider RINEX Version 4 in future

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