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

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

bug fixed in RINEX v2.11 header

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