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

Last change on this file since 7790 was 7790, checked in by stuerze, 3 years ago

minor additions regarding default GLONASS obs types

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