source: ntrip/branches/BNC_2.12/src/rinex/rnxobsfile.cpp @ 8113

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

set default rinex version 3 obs types if skl file obs types are of version 2 and rinex output file shall be in version 3 in order to get 3char obs types as required in rinex version 3.03

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