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

Last change on this file since 8167 was 8167, checked in by stuerze, 2 years ago

IRNSS support is added in RINEX QC

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