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

Last change on this file since 8628 was 8628, checked in by stuerze, 16 months ago

some updates to support RINEX Version 3.04

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