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

Last change on this file since 9770 was 9770, checked in by stuerze, 8 weeks ago

minor changes to handle RINEX 2,3,4

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