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

Last change on this file since 7803 was 7803, checked in by stuerze, 4 years ago

minor changes to remove duplicates in rinex header comments

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