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

Last change on this file was 8745, checked in by stuerze, 4 months ago

updated default observation types for Galileo

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