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

Last change on this file since 8204 was 8204, checked in by wiese, 2 years ago

CHANGE: #105 toAscii deprecated

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