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

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

minor changes

File size: 47.4 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
1306  for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1307    const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1308    char            sys    = rnxSat.prn.system();
1309    for (int iTypeV2 = 0; iTypeV2 < header.nTypes(sys); iTypeV2++) {
1310      if (iTypeV2 > 0 && iTypeV2 % 5 == 0) {
1311        *stream << endl;
1312      }
1313      QString typeV2 = header.obsType(sys, iTypeV2);
1314      bool    found  = false;
1315      QStringList preferredAttribList = signalPriorities(sys);
1316      QString preferredAttrib;
1317      for (int ii = 0; ii < preferredAttribList.size(); ii++) {
1318        if (preferredAttribList[ii].indexOf("&") != -1) {
1319          QStringList hlp = preferredAttribList[ii].split("&", QString::SkipEmptyParts);
1320          if (hlp.size() == 2 && hlp[0].contains(typeV2[1])) {
1321            preferredAttrib = hlp[1];
1322          }
1323        }
1324        else {
1325          preferredAttrib = preferredAttribList[ii];
1326        }
1327      }
1328
1329      for (int iPref = 0; iPref < preferredAttrib.size(); iPref++) {
1330        QMapIterator<QString, t_rnxObs> itObs(rnxSat.obs);
1331        while (itObs.hasNext()) {
1332          itObs.next();
1333          const QString&  type   = itObs.key();
1334          const t_rnxObs& rnxObs = itObs.value();
1335          if ( preferredAttrib[iPref] == '?'                             ||
1336               (type.length() == 2 && preferredAttrib[iPref] == '_'    ) ||
1337               (type.length() == 3 && preferredAttrib[iPref] == type[2]) ) {
1338            if (typeV2 == type3to2(sys, type)) {
1339              found = true;
1340              if (rnxObs.value == 0.0) {
1341                *stream << QString().leftJustified(16);
1342              }
1343              else {
1344                *stream << QString("%1").arg(rnxObs.value, 14, 'f', 3);
1345                if (rnxObs.lli != 0.0) {
1346                  *stream << QString("%1").arg(rnxObs.lli,1);
1347                }
1348                else {
1349                  *stream << ' ';
1350                }
1351                if (rnxObs.snr != 0.0) {
1352                  *stream << QString("%1").arg(rnxObs.snr,1);
1353                }
1354                else {
1355                  *stream << ' ';
1356                }
1357              }
1358              goto end_loop_iPref;
1359            }
1360          }
1361        }
1362      } end_loop_iPref:
1363      if (!found) {
1364        *stream << QString().leftJustified(16);
1365      }
1366    }
1367    *stream << endl;
1368  }
1369}
1370
1371// Write Data Epoch (RINEX Version 3)
1372////////////////////////////////////////////////////////////////////////////
1373void t_rnxObsFile::writeEpochV3(QTextStream* stream, const t_rnxObsHeader& header,
1374                                const t_rnxEpo* epo) {
1375
1376  unsigned year, month, day, hour, min;
1377  double sec;
1378  epo->tt.civil_date(year, month, day);
1379  epo->tt.civil_time(hour, min, sec);
1380
1381  QString dateStr;
1382  QTextStream(&dateStr) << QString("> %1 %2 %3 %4 %5%6")
1383      .arg(year, 4)
1384      .arg(month, 2, 10, QChar('0'))
1385      .arg(day, 2, 10, QChar('0'))
1386      .arg(hour, 2, 10, QChar('0'))
1387      .arg(min, 2, 10, QChar('0'))
1388      .arg(sec, 11, 'f', 7);
1389
1390  int flag = 0;
1391  *stream << dateStr << QString("%1%2\n").arg(flag, 3).arg(epo->rnxSat.size(), 3);
1392
1393  for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1394    const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1395    char sys = rnxSat.prn.system();
1396
1397    const t_rnxObs* hlp[header.nTypes(sys)];
1398    for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
1399      hlp[iTypeV3] = 0;
1400      QString typeV3 = header.obsType(sys, iTypeV3);
1401      QMapIterator<QString, t_rnxObs> itObs(rnxSat.obs);
1402
1403      // Exact match
1404      // -----------
1405      while (itObs.hasNext()) {
1406        itObs.next();
1407        const QString& type = itObs.key();
1408        const t_rnxObs& rnxObs = itObs.value();
1409        if (typeV3 == type2to3(sys, type) && rnxObs.value != 0.0) {
1410          hlp[iTypeV3] = &itObs.value();
1411        }
1412      }
1413
1414      // Non-Exact match
1415      // ---------------
1416      itObs.toFront();
1417      while (itObs.hasNext()) {
1418        itObs.next();
1419        const QString& type = itObs.key();
1420        const t_rnxObs& rnxObs = itObs.value();
1421        if (hlp[iTypeV3] == 0 && typeV3 == type2to3(sys, type).left(2) && rnxObs.value != 0.0) {
1422          hlp[iTypeV3] = &itObs.value();
1423        }
1424      }
1425    }
1426
1427    if (header.nTypes(sys)) {
1428      *stream << rnxSat.prn.toString().c_str();
1429      for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
1430        const t_rnxObs* rnxObs = hlp[iTypeV3];
1431        if (rnxObs == 0) {
1432          *stream << QString().leftJustified(16);
1433        }
1434        else {
1435          *stream << QString("%1").arg(rnxObs->value, 14, 'f', 3);
1436          if (rnxObs->lli != 0.0) {
1437            *stream << QString("%1").arg(rnxObs->lli, 1);
1438          }
1439          else {
1440            *stream << ' ';
1441          }
1442          if (rnxObs->snr != 0.0) {
1443            *stream << QString("%1").arg(rnxObs->snr, 1);
1444          }
1445          else {
1446            *stream << ' ';
1447          }
1448        }
1449      }
1450      *stream << endl;
1451    }
1452  }
1453}
1454
1455// Translate Observation Type v2 --> v3
1456////////////////////////////////////////////////////////////////////////////
1457QString t_rnxObsFile::type2to3(char sys, const QString& typeV2) {
1458  if      (typeV2 == "P1") {
1459    return (sys == 'G') ? "C1W" : "C1P";
1460  }
1461  else if (typeV2 == "P2") {
1462    return (sys == 'G') ? "C2W" : "C2P";
1463  }
1464  return typeV2;
1465}
1466
1467// Translate Observation Type v3 --> v2
1468////////////////////////////////////////////////////////////////////////////
1469QString t_rnxObsFile::type3to2(char /* sys */, const QString& typeV3) {
1470  if      (typeV3 == "C1P" || typeV3 == "C1W") {
1471    return "P1";
1472  }
1473  else if (typeV3 == "C2P" || typeV3 == "C2W") {
1474    return "P2";
1475  }
1476  return typeV3.left(2);
1477}
1478
1479// Set Observations from RINEX File
1480////////////////////////////////////////////////////////////////////////////
1481void t_rnxObsFile::setObsFromRnx(const t_rnxObsFile* rnxObsFile, const t_rnxObsFile::t_rnxEpo* epo,
1482                                 const t_rnxObsFile::t_rnxSat& rnxSat, t_satObs& obs) {
1483  obs._staID = rnxObsFile->markerName().toAscii().constData();
1484  obs._prn   = rnxSat.prn;
1485  obs._time  = epo->tt;
1486
1487  char sys   = rnxSat.prn.system();
1488
1489  QChar addToL2;
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 (rnxSat.obs.contains(type) && rnxSat.obs[type].value != 0.0) {
1494      if (type == "P2" && typeV3.length() > 2) {
1495        addToL2 = typeV3[2];
1496        break;
1497      }
1498    }
1499  }
1500
1501  for (int iType = 0; iType < rnxObsFile->nTypes(sys); iType++) {
1502    QString type   = rnxObsFile->obsType(sys, iType);
1503    QString typeV3 = rnxObsFile->obsType(sys, iType, 3.0); // may or may not differ from type
1504    if (type == "L2") {
1505      typeV3 += addToL2;
1506    }
1507    if (rnxSat.obs.contains(type)) {
1508      const t_rnxObs& rnxObs = rnxSat.obs[type];
1509      if (rnxObs.value != 0.0) {
1510        string type2ch(typeV3.mid(1).toAscii().data());
1511
1512        t_frqObs* frqObs = 0;
1513        for (unsigned iFrq = 0; iFrq < obs._obs.size(); iFrq++) {
1514          if (obs._obs[iFrq]->_rnxType2ch == type2ch) {
1515            frqObs = obs._obs[iFrq];
1516            break;
1517          }
1518        }
1519        if (frqObs == 0) {
1520          frqObs = new t_frqObs;
1521          frqObs->_rnxType2ch = type2ch;
1522          obs._obs.push_back(frqObs);
1523        }
1524
1525        switch( typeV3.toAscii().data()[0] ) {
1526        case 'C':
1527          frqObs->_codeValid = true;
1528          frqObs->_code      = rnxObs.value;
1529          break;
1530        case 'L':
1531          frqObs->_phaseValid = true;
1532          frqObs->_phase      = rnxObs.value;
1533          frqObs->_slip       = (rnxObs.lli & 1);
1534          break;
1535        case 'D':
1536          frqObs->_dopplerValid = true;
1537          frqObs->_doppler      = rnxObs.value;
1538          break;
1539        case 'S':
1540          frqObs->_snrValid = true;
1541          frqObs->_snr      = rnxObs.value;
1542          break;
1543        }
1544
1545        // Handle old-fashioned SNR values
1546        // -------------------------------
1547        if (rnxObs.snr != 0 && !frqObs->_snrValid) {
1548          frqObs->_snrValid = true;
1549          frqObs->_snr      = rnxObs.snr * 6.0 + 2.5;
1550        }
1551      }
1552    }
1553  }
1554}
1555
1556// Tracking Mode Priorities
1557////////////////////////////////////////////////////////////////////////////
1558QStringList t_rnxObsFile::signalPriorities(char sys) {
1559
1560  bncSettings settings;
1561
1562  QStringList priorList;
1563  QString reqcAction = settings.value("reqcAction").toString();
1564
1565  // Priorities in Edit/Concatenate (post processing) mode
1566  // ---------------------------------------------------
1567  if (reqcAction == "Edit/Concatenate") {
1568    priorList = settings.value("reqcV2Priority").toString().split(" ", QString::SkipEmptyParts);
1569  }
1570
1571  // Priorities in real-time mode
1572  // ----------------------------
1573  else {
1574    priorList = settings.value("rnxV2Priority").toString().split(" ", QString::SkipEmptyParts);
1575  }
1576
1577  QStringList result;
1578  for (int ii = 0; ii < priorList.size(); ii++) {
1579    if (priorList[ii].indexOf(":") != -1) {
1580      QStringList hlp = priorList[ii].split(":", QString::SkipEmptyParts);
1581      if (hlp.size() == 2 && hlp[0].length() == 1 && hlp[0][0] == sys) {
1582        result.append(hlp[1]);
1583      }
1584    }
1585    else {
1586      result.append(priorList[ii]);
1587    }
1588  }
1589
1590  if (result.empty()) {
1591    switch (sys) {
1592      case 'G':
1593        result.append("12&PWCSLXYN");
1594        result.append("5&IQX");
1595        break;
1596      case 'R':
1597        result.append("12&PC");
1598        result.append("3&IQX");
1599        break;
1600      case 'E':
1601        result.append("16&BCX");
1602        result.append("578&IQX");
1603        break;
1604      case 'J':
1605        result.append("1&SLXCZ");
1606        result.append("26&SLX");
1607        result.append("5&IQX");
1608        break;
1609      case 'C':
1610        result.append("IQX");
1611        break;
1612      case 'I':
1613        result.append("ABCX");
1614        break;
1615      case 'S':
1616        result.append("1&C");
1617        result.append("5&IQX");
1618        break;
1619    }
1620  }
1621  return result;
1622}
Note: See TracBrowser for help on using the repository browser.