source: ntrip/branches/BNC_2.12/src/rinex/rnxobsfile.cpp @ 8802

Last change on this file since 8802 was 8802, checked in by stuerze, 11 months ago

minor changes

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