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

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

minor changes regarding obs types

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