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

Last change on this file since 8629 was 8629, checked in by stuerze, 5 weeks ago

some updates to support RINEX Version 3.04

File size: 48.8 KB
Line 
1// Part of BNC, a utility for retrieving decoding and
2// converting GNSS data streams from NTRIP broadcasters.
3//
4// Copyright (C) 2007
5// German Federal Agency for Cartography and Geodesy (BKG)
6// http://www.bkg.bund.de
7// Czech Technical University Prague, Department of Geodesy
8// http://www.fsv.cvut.cz
9//
10// Email: euref-ip@bkg.bund.de
11//
12// This program is free software; you can redistribute it and/or
13// modify it under the terms of the GNU General Public License
14// as published by the Free Software Foundation, version 2.
15//
16// This program is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with this program; if not, write to the Free Software
23// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25/* -------------------------------------------------------------------------
26 * BKG NTRIP Client
27 * -------------------------------------------------------------------------
28 *
29 * Class:      t_rnxObsFile
30 *
31 * Purpose:    Reads RINEX Observation File
32 *
33 * Author:     L. Mervart
34 *
35 * Created:    24-Jan-2012
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <iostream>
42#include <iomanip>
43#include <sstream>
44#include "rnxobsfile.h"
45#include "bncutils.h"
46#include "bnccore.h"
47#include "bncsettings.h"
48
49using namespace std;
50
51// Constructor
52////////////////////////////////////////////////////////////////////////////
53t_rnxObsHeader::t_rnxObsHeader() {
54  _usedSystems = "GREJCSI";
55  _antNEU.ReSize(3); _antNEU = 0.0;
56  _antXYZ.ReSize(3); _antXYZ = 0.0;
57  _antBSG.ReSize(3); _antBSG = 0.0;
58  _xyz.ReSize(3);    _xyz    = 0.0;
59  _version  = 0.0;
60  _interval = 0.0;
61  for (unsigned iPrn = 1; iPrn <= t_prn::MAXPRN_GPS; iPrn++) {
62    _wlFactorsL1[iPrn] = 1;
63    _wlFactorsL2[iPrn] = 1;
64  }
65  bncSettings settings;
66  _writeRinexOnlyWithSklObsTypes = settings.value("rnxOnlyWithSKL").toBool();
67}
68
69// Destructor
70////////////////////////////////////////////////////////////////////////////
71t_rnxObsHeader::~t_rnxObsHeader() {
72}
73
74// Read Header
75////////////////////////////////////////////////////////////////////////////
76t_irc t_rnxObsHeader::read(QTextStream* stream, int maxLines) {
77  _comments.clear();
78  int numLines = 0;
79
80  while ( stream->status() == QTextStream::Ok && !stream->atEnd() ) {
81    QString line = stream->readLine(); ++ numLines;
82    if (line.isEmpty()) {
83      continue;
84    }
85    if (line.indexOf("END OF FILE") != -1) {
86      break;
87    }
88    QString value = line.mid(0,60).trimmed();
89    QString key   = line.mid(60).trimmed();
90    if      (key == "END OF HEADER") {
91      break;
92    }
93    else if (key == "RINEX VERSION / TYPE") {
94      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
95      in >> _version;
96    }
97    else if (key == "MARKER NAME") {
98      _markerName = value;
99    }
100    else if (key == "MARKER TYPE") {
101      _markerType = value;
102    }
103    else if (key == "MARKER NUMBER") {
104      _markerNumber = line.mid(0,20).trimmed();
105    }
106    else if (key == "ANT # / TYPE") {
107      _antennaNumber = line.mid( 0,20).trimmed();
108      _antennaName   = line.mid(20,20).trimmed();
109    }
110    else if (key == "OBSERVER / AGENCY") {
111      _observer = line.mid( 0,20).trimmed();
112      _agency   = line.mid(20,40).trimmed();
113    }
114    else if (key == "REC # / TYPE / VERS") {
115      _receiverNumber  = line.mid( 0,20).trimmed();
116      _receiverType    = line.mid(20,20).trimmed();
117      _receiverVersion = line.mid(40,20).trimmed();
118    }
119    else if (key == "INTERVAL") {
120      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
121      in >> _interval;
122    }
123    else if (key == "COMMENT") {
124      _comments << line.mid(0,60).trimmed();
125    }
126    else if (key == "WAVELENGTH FACT L1/2") {
127      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
128      int wlFactL1 = 0;
129      int wlFactL2 = 0;
130      int numSat   = 0;
131      in >> wlFactL1 >> wlFactL2 >> numSat;
132      if (numSat == 0) {
133        for (unsigned iPrn = 1; iPrn <= t_prn::MAXPRN_GPS; iPrn++) {
134          _wlFactorsL1[iPrn] = wlFactL1;
135          _wlFactorsL2[iPrn] = wlFactL2;
136        }
137      }
138      else {
139        for (int ii = 0; ii < numSat; ii++) {
140          QString prn; in >> prn;
141          if (prn[0] == 'G') {
142            int iPrn;
143            readInt(prn, 1, 2, iPrn);
144            _wlFactorsL1[iPrn] = wlFactL1;
145            _wlFactorsL2[iPrn] = wlFactL2;
146          }
147        }
148      }
149    }
150    else if (key == "APPROX POSITION XYZ") {
151      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
152      in >> _xyz[0] >> _xyz[1] >> _xyz[2];
153    }
154    else if (key == "ANTENNA: DELTA H/E/N") {
155      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
156      in >> _antNEU[2] >> _antNEU[1] >> _antNEU[0];
157    }
158    else if (key == "ANTENNA: DELTA X/Y/Z") {
159      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
160      in >> _antXYZ[0] >> _antXYZ[1] >> _antXYZ[2];
161    }
162    else if (key == "ANTENNA: B.SIGHT XYZ") {
163      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
164      in >> _antBSG[0] >> _antBSG[1] >> _antBSG[2];
165    }
166    else if (key == "# / TYPES OF OBSERV") {
167      if (_version == 0.0) {
168        _version = defaultRnxObsVersion2;
169      }
170      QTextStream* in = new QTextStream(value.toLatin1(), QIODevice::ReadOnly);
171      int nTypes;
172      *in >> nTypes;
173      char sys0 = _usedSystems[0].toLatin1();
174      _obsTypes[sys0].clear();
175      for (int ii = 0; ii < nTypes; ii++) {
176        if (ii > 0 && ii % 9 == 0) {
177          line = stream->readLine(); ++numLines;
178          delete in;
179          in = new QTextStream(line.left(60).toLatin1(), QIODevice::ReadOnly);
180        }
181        QString hlp;
182        *in >> hlp;
183        _obsTypes[sys0].append(hlp);
184      }
185      for (int ii = 1; ii < _usedSystems.length(); ii++) {
186        char sysI = _usedSystems[ii].toLatin1();
187        _obsTypes[sysI] = _obsTypes[sys0];
188      }
189    }
190    else if (key == "SYS / # / OBS TYPES") {
191      if (_version == 0.0) {
192        _version = defaultRnxObsVersion3;
193      }
194      QTextStream* in = new QTextStream(value.toLatin1(), QIODevice::ReadOnly);
195      char sys;
196      int nTypes;
197      *in >> sys >> nTypes;
198      _obsTypes[sys].clear();
199      for (int ii = 0; ii < nTypes; ii++) {
200        if (ii > 0 && ii % 13 == 0) {
201          line = stream->readLine(); ++numLines;
202          delete in;
203          in = new QTextStream(line.toLatin1(), QIODevice::ReadOnly);
204        }
205        QString hlp;
206        *in >> hlp;
207        if (sys == 'C' && _version < 3.03)  {
208          hlp.replace('1', '2');
209        }
210        _obsTypes[sys].push_back(hlp);
211      }
212      delete in;
213    }
214    else if (key == "TIME OF FIRST OBS") {
215      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
216      int year, month, day, hour, min;
217      double sec;
218      in >> year >> month >> day >> hour >> min >> sec;
219      _startTime.set(year, month, day, hour, min, sec);
220    }
221    else if (key == "SYS / PHASE SHIFT"){
222      QTextStream* in = new QTextStream(value.toLatin1(), QIODevice::ReadOnly);
223      char        sys;
224      QString     obstype;
225      double      shift;
226      int         satnum = 0;
227      QStringList satList;
228      QString     sat;
229      *in >> sys >> obstype >> shift >> satnum;
230      if (obstype.size()) {
231        for (int ii = 0; ii < satnum; ii++) {
232          if (ii > 0 && ii % 10 == 0) {
233            line = stream->readLine(); ++numLines;
234            delete in;
235            in = new QTextStream(line.left(60).toLatin1(), QIODevice::ReadOnly);
236          }
237          *in >> sat;
238          satList.append(sat);
239        }
240        delete in;
241      }
242      _phaseShifts.insert(sys+obstype, QPair<double, QStringList>(shift, satList));
243    }
244    else if (key == "GLONASS COD/PHS/BIS"){
245      QTextStream in(value.toLatin1(), QIODevice::ReadOnly);
246      for (int ii = 0; ii < 4; ii++) {
247        QString type;
248        double  value;
249        in >> type >> value;
250        if (type.size())
251          _gloBiases[type] = value;
252      }
253    }
254    else if (key == "GLONASS SLOT / FRQ #") {
255      QTextStream* in = new QTextStream(value.toLatin1(), QIODevice::ReadOnly);
256      int nSlots = 0;
257      *in >> nSlots;
258      for (int ii = 0; ii < nSlots; ii++) {
259        if (ii > 0 && ii % 8 == 0) {
260          line = stream->readLine(); ++numLines;
261          delete in;
262          in = new QTextStream(line.left(60).toLatin1(), QIODevice::ReadOnly);
263        }
264        QString sat;
265        int    slot;
266        *in >> sat >> slot;
267        t_prn prn;
268        prn.set(sat.toStdString());
269        if(sat.size())
270          _gloSlots[prn] = slot;
271      }
272      delete in;
273    }
274    if (maxLines > 0 && numLines == maxLines) {
275      break;
276    }
277  }
278
279  // set default observation types if empty in input file
280  // ----------------------------------------------------
281  if (_obsTypes.empty()) {
282    if (!_writeRinexOnlyWithSklObsTypes) {
283      setDefault(_markerName, _version);
284    }
285    else {
286      return failure;
287    }
288  }
289
290  // Systems used
291  // ------------
292  _usedSystems.clear();
293  QMapIterator<char, QStringList> it(_obsTypes);
294  while (it.hasNext()) {
295    it.next();
296    _usedSystems += QChar(it.key());
297  }
298
299  return success;
300}
301
302// Set Default Header
303////////////////////////////////////////////////////////////////////////////
304void t_rnxObsHeader::setDefault(const QString& markerName, int version) {
305
306  _markerName = markerName;
307
308  if (version <= 2) {
309    _version = defaultRnxObsVersion2;
310  }
311  else {
312    _version = defaultRnxObsVersion3;
313  }
314
315  _comments << "Default set of observation types used";
316  _comments.removeDuplicates();
317
318  _obsTypes.clear();
319  if (_version < 3.0) {
320    _obsTypes['G'] << "C1" << "P1" << "L1" << "S1"
321                   << "C2" << "P2" << "L2" << "S2";
322    _obsTypes['R'] = _obsTypes['G'];
323    _obsTypes['E'] = _obsTypes['G'];
324    _obsTypes['J'] = _obsTypes['G'];
325    _obsTypes['S'] = _obsTypes['G'];
326    _obsTypes['C'] = _obsTypes['G'];
327  }
328  else {
329    _obsTypes['G'] << "C1C" << "L1C"  << "S1C"
330                   << "C1W" << "L1W"  << "S1W"
331                   << "C2X" << "L2X"  << "S2X"
332                   << "C2W" << "L2W"  << "S2W"
333                   << "C5X" << "L5X"  << "S5X";
334
335    _obsTypes['J'] << "C1C" << "L1C"  << "S1C"
336                   << "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      else {
457        for (int iSys = 0; iSys < header.numSys(); iSys++) {
458          char sys = header.system(iSys);
459          for (int iType = 0; iType < header.nTypes(sys); iType++) {
460            QString type = header.obsType(sys, iType, _version);
461            for (int jSys = 0; jSys < _usedSystems.length(); jSys++) {
462              char thisSys  = _usedSystems[jSys].toLatin1();
463              if (!_obsTypes[thisSys].contains(type)) {
464                _obsTypes[thisSys].push_back(type);
465              }
466            }
467          }
468        }
469      }
470    }
471  }
472  else {
473    for (int iType = 0; iType < useObsTypes->size(); iType++) {
474      if (useObsTypes->at(iType).indexOf(":") != -1) {
475        QStringList hlp = useObsTypes->at(iType).split(":", QString::SkipEmptyParts);
476        if (hlp.size() == 2 && hlp[0].length() == 1) {
477          if (_version >= 3.0) {
478            char    sys  = hlp[0][0].toLatin1();
479            QString type = t_rnxObsFile::type2to3(sys, hlp[1]);
480            if (!_obsTypes[sys].contains(type)) {
481              _obsTypes[sys].push_back(type);
482            }
483          }
484          else {
485            for (int iSys = 0; iSys < _usedSystems.length(); iSys++) {
486              char    sys  = _usedSystems[iSys].toLatin1();
487              QString type = t_rnxObsFile::type3to2(sys, hlp[1]);
488              if (!_obsTypes[sys].contains(type)) {
489                _obsTypes[sys].push_back(type);
490              }
491            }
492          }
493        }
494      }
495      else {
496        for (int iSys = 0; iSys < _usedSystems.length(); iSys++) {
497          char    sys  = _usedSystems[iSys].toLatin1();
498          QString type = _version >= 3.0 ? t_rnxObsFile::type2to3(sys, useObsTypes->at(iType)) :
499                                           t_rnxObsFile::type3to2(sys, useObsTypes->at(iType));
500          if (!_obsTypes[sys].contains(type)) {
501            _obsTypes[sys].push_back(type);
502          }
503        }
504      }
505    }
506    _usedSystems.clear();
507    QMapIterator<char, QStringList> it(_obsTypes);
508    while (it.hasNext()) {
509      it.next();
510      _usedSystems += QChar(it.key());
511    }
512  }
513
514  if (_version >= 3.0) {
515    // set phase shifts
516    if (!phaseShifts ||  phaseShifts->empty()) {
517      _phaseShifts = header._phaseShifts;
518    }
519    else {
520      foreach (const QString &str, *phaseShifts) {
521        QStringList hlp  = str.split("_", QString::SkipEmptyParts);
522        QStringList hlp1 = hlp.last().split(":", QString::SkipEmptyParts);
523        QString type = hlp.first();
524        double shift = hlp1.first().toDouble();
525        hlp1.removeFirst();
526        QStringList &satList = hlp1;
527        QMap<QString, QPair<double, QStringList> >::iterator it = _phaseShifts.find(type);
528        if ( it != _phaseShifts.end()) {
529          it.value().second.append(satList);
530          it.value().second.removeDuplicates();
531        }
532        else {
533          _phaseShifts.insert(type, QPair<double, QStringList>(shift, satList));
534        }
535      }
536    }
537    // set GLONASS biases
538    if (!gloBiases || gloBiases->empty()) {
539      _gloBiases = header._gloBiases;
540    }
541    else {
542      foreach (const QString &str, *gloBiases) {
543        QStringList hlp = str.split(":", QString::SkipEmptyParts);
544        QString type = hlp.first();;
545        double  value = hlp.last().toDouble();
546        if (type.size())
547          _gloBiases[type] = value;
548      }
549    }
550    // set GLONASS slots
551    if (!gloSlots  || gloSlots->empty()) {
552      _gloSlots = header._gloSlots;
553    }
554    else {
555      foreach (const QString &str, *gloSlots) {
556        QStringList hlp = str.split(":", QString::SkipEmptyParts);
557        QString sat = hlp.first();
558        int    slot = hlp.last().toInt();
559        t_prn prn;
560        prn.set(sat.toStdString());
561        if(sat.size())
562          _gloSlots[prn] = slot;
563      }
564    }
565  }
566}
567
568// Write Header
569////////////////////////////////////////////////////////////////////////////
570void t_rnxObsHeader::write(QTextStream* stream,
571                           const QMap<QString, QString>* txtMap) const {
572
573  QStringList newComments;
574  QString     runBy = BNC_CORE->userName();
575
576  if (txtMap) {
577   QMapIterator<QString, QString> it(*txtMap);
578    while (it.hasNext()) {
579      it.next();
580      if      (it.key() == "RUN BY") {
581        runBy = it.value();
582      }
583      else if ((it.key().indexOf("COMMENT")) != -1) {
584        newComments += it.value().split("\\n", QString::SkipEmptyParts);
585      }
586    }
587    newComments.removeDuplicates();
588  }
589
590  *stream << QString("%1           OBSERVATION DATA    M")
591    .arg(_version, 9, 'f', 2)
592    .leftJustified(60)
593           << "RINEX VERSION / TYPE\n";
594
595  const QString fmtDate = (_version < 3.0) ? "dd-MMM-yy hh:mm"
596                                                  : "yyyyMMdd hhmmss UTC";
597  *stream << QString("%1%2%3")
598    .arg(BNC_CORE->pgmName(), -20)
599    .arg(runBy.trimmed().left(20), -20)
600    .arg(QDateTime::currentDateTime().toUTC().toString(fmtDate), -20)
601    .leftJustified(60)
602           << "PGM / RUN BY / DATE\n";
603
604  QStringListIterator itCmnt(_comments + newComments);
605  while (itCmnt.hasNext()) {
606    *stream << itCmnt.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
607  }
608
609  *stream << QString("%1")
610    .arg(_markerName, -60)
611    .leftJustified(60)
612           << "MARKER NAME\n";
613
614  if (!_markerNumber.isEmpty()) {
615    *stream << QString("%1")
616      .arg(_markerNumber, -20)
617      .leftJustified(60)
618             << "MARKER NUMBER\n";
619  }
620
621  if (_version >= 3.0) {
622    *stream << QString("%1")
623      .arg(_markerType, -60)
624      .leftJustified(60)
625             << "MARKER TYPE\n";
626  }
627
628  *stream << QString("%1%2")
629    .arg(_observer, -20)
630    .arg(_agency,   -40)
631    .leftJustified(60)
632           << "OBSERVER / AGENCY\n";
633
634  *stream << QString("%1%2%3")
635    .arg(_receiverNumber,  -20)
636    .arg(_receiverType,    -20)
637    .arg(_receiverVersion, -20)
638    .leftJustified(60)
639           << "REC # / TYPE / VERS\n";
640
641  *stream << QString("%1%2")
642    .arg(_antennaNumber, -20)
643    .arg(_antennaName,   -20)
644    .leftJustified(60)
645           << "ANT # / TYPE\n";
646
647  *stream << QString("%1%2%3")
648    .arg(_xyz(1), 14, 'f', 4)
649    .arg(_xyz(2), 14, 'f', 4)
650    .arg(_xyz(3), 14, 'f', 4)
651    .leftJustified(60)
652           << "APPROX POSITION XYZ\n";
653
654  *stream << QString("%1%2%3")
655    .arg(_antNEU(3), 14, 'f', 4)
656    .arg(_antNEU(2), 14, 'f', 4)
657    .arg(_antNEU(1), 14, 'f', 4)
658    .leftJustified(60)
659           << "ANTENNA: DELTA H/E/N\n";
660
661  if (_version < 3.0) {
662    int defaultWlFact1 = _wlFactorsL1[1];
663    int defaultWlFact2 = _wlFactorsL2[1];  // TODO check all prns
664    *stream << QString("%1%2")
665      .arg(defaultWlFact1, 6)
666      .arg(defaultWlFact2, 6)
667      .leftJustified(60)
668             << "WAVELENGTH FACT L1/2\n";
669  }
670
671  *stream << obsTypesStrings().join("");
672
673  if (_interval > 0) {
674    *stream << QString("%1")
675      .arg(_interval, 10, 'f', 3)
676      .leftJustified(60)
677             << "INTERVAL\n";
678  }
679
680  unsigned year, month, day, hour, min;
681  double sec;
682  _startTime.civil_date(year, month, day);
683  _startTime.civil_time(hour, min, sec);
684  *stream << QString("%1%2%3%4%5%6%7")
685    .arg(year, 6)
686    .arg(month, 6)
687    .arg(day, 6)
688    .arg(hour, 6)
689    .arg(min, 6)
690    .arg(sec, 13, 'f', 7)
691    .arg("GPS", 8)
692    .leftJustified(60)
693           << "TIME OF FIRST OBS\n";
694
695  if (_version >= 3.0) {
696    if (_phaseShifts.empty()) {
697      QMap<char, QStringList>::const_iterator it;
698      for (it = _obsTypes.begin(); it != _obsTypes.end(); ++it) {
699        char sys = it.key();
700        double shift = 0.0;
701        foreach (const QString &obstype, it.value()) {
702          if (obstype.left(1).contains('L')) {
703          *stream << QString("%1%2%3")
704            .arg(sys, 0)
705            .arg(obstype, 4)
706            .arg(shift, 9, 'f', 5)
707            .leftJustified(60)
708             << "SYS / PHASE SHIFT\n";
709          }
710        }
711      }
712    } else {
713      QMap<QString, QPair<double, QStringList> >::const_iterator it;
714      QString emptyFillStr;
715      for(it = _phaseShifts.begin(); it!= _phaseShifts.end(); ++it) {
716        QString sys         = it.key().left(1);
717        QString obstype     = it.key().mid(1);
718        double shift        = it.value().first;
719        QStringList satList = it.value().second;
720        QString hlp;
721        if (obstype.isEmpty()) {
722          hlp = QString("%1")
723            .arg(sys.toStdString().c_str(), 0);
724        }
725        else {
726          hlp = QString("%1%2%3")
727            .arg(sys.toStdString().c_str(), 0)
728            .arg(obstype, 4)
729            .arg(shift, 9, 'f', 5);
730        }
731        if (!satList.empty()) {
732          hlp += QString("%1").arg(satList.size(), 4);
733        }
734        else {
735          *stream << QString("%1")
736            .arg(hlp, 0)
737            .leftJustified(60)
738             << "SYS / PHASE SHIFT\n";
739          hlp = "";
740        }
741        int ii = 0;
742        QStringList::const_iterator it_s;
743        for (it_s = satList.begin(); it_s != satList.end(); ++it_s) {
744          (hlp.contains(obstype)) ?
745            emptyFillStr = "": emptyFillStr = "                  ";
746          hlp += QString("%1").arg(*it_s, 4);
747          ii++;
748          if (ii % 10 == 0) {
749            *stream << QString("%1%2")
750              .arg(emptyFillStr, 0)
751              .arg(hlp, 0)
752              .leftJustified(60)
753              << "SYS / PHASE SHIFT\n";
754            hlp = "";
755          }
756        }
757        if (hlp.size()) {
758          (hlp.contains(obstype)) ?
759            emptyFillStr = "": emptyFillStr = "                  ";
760        *stream <<  QString("%1%2")
761          .arg(emptyFillStr, 0)
762          .arg(hlp, 0)
763          .leftJustified(60)
764          << "SYS / PHASE SHIFT\n";
765        }
766      }
767    }
768  }
769
770  if (_version >= 3.0) {
771    QString hlp = "";
772    QMap<QString, double>::const_iterator it = _gloBiases.begin();
773    while (it != _gloBiases.end()){
774      hlp += QString("%1%2").arg(it.key(), 4).arg(it.value(), 9, 'f', 3);
775      it++;
776    }
777    *stream << QString("%1")
778      .arg(hlp, 0)
779      .leftJustified(60)
780           << "GLONASS COD/PHS/BIS\n";
781  }
782
783  if (_version >= 3.0) {
784    QString number = QString::number(_gloSlots.size());
785    QString hlp = "";
786    int ii = 0;
787    QMap<t_prn, int>::const_iterator it = _gloSlots.begin();
788    while (it != _gloSlots.end()) {
789      QString prn(it.key().toString().c_str());
790      hlp +=  QString("%1%2").arg(prn, 4).arg(it.value(), 3);
791      it++;
792      ii++;
793      if (ii % 8 == 0) {
794        *stream << QString("%1%2")
795          .arg(number, 3)
796          .arg(hlp, 0)
797          .leftJustified(60)
798           << "GLONASS SLOT / FRQ #\n";
799        ii = 0;
800        hlp = number = "";
801      }
802    }
803    if (hlp.size() || !_gloSlots.size()) {
804      *stream << QString("%1%2")
805            .arg(number, 3)
806            .arg(hlp, 0)
807            .leftJustified(60)
808            << "GLONASS SLOT / FRQ #\n";
809    }
810  }
811
812  *stream << QString()
813    .leftJustified(60)
814           << "END OF HEADER\n";
815}
816
817// Number of Different Systems
818////////////////////////////////////////////////////////////////////////////
819int t_rnxObsHeader::numSys() const {
820  return _obsTypes.size();
821}
822
823//
824////////////////////////////////////////////////////////////////////////////
825char t_rnxObsHeader::system(int iSys) const {
826  int iSysLocal = -1;
827  QMapIterator<char, QStringList> it(_obsTypes);
828  while (it.hasNext()) {
829    ++iSysLocal;
830    it.next();
831    if (iSysLocal == iSys) {
832      return it.key();
833    }
834  }
835  return ' ';
836}
837
838//
839////////////////////////////////////////////////////////////////////////////
840QString t_rnxObsHeader::usedSystems(void) const {
841  return _usedSystems;
842}
843
844QStringList t_rnxObsHeader::obsTypes(char sys) const {
845  if (_obsTypes.contains(sys)) {
846    return _obsTypes[sys];
847  }
848  else {
849    return QStringList();
850  }
851}
852
853// Number of Observation Types (satellite-system specific)
854////////////////////////////////////////////////////////////////////////////
855int t_rnxObsHeader::nTypes(char sys) const {
856  if (_obsTypes.contains(sys)) {
857    return _obsTypes[sys].size();
858  }
859  else {
860    return 0;
861  }
862}
863
864// Number of GLONASS biases
865////////////////////////////////////////////////////////////////////////////
866int t_rnxObsHeader::numGloBiases() const {
867  return _gloBiases.size();
868}
869
870// Number of GLONASS slots
871////////////////////////////////////////////////////////////////////////////
872int t_rnxObsHeader::numGloSlots() const {
873  return _gloSlots.size();
874}
875
876// Observation Type (satellite-system specific)
877////////////////////////////////////////////////////////////////////////////
878QString t_rnxObsHeader::obsType(char sys, int index, double version) const {
879
880  if (version == 0.0) {
881    version = _version;
882  }
883  if (_obsTypes.contains(sys)) {
884    QString origType = _obsTypes[sys].at(index);
885    if      (int(version) == int(_version)) {
886      return origType;
887    }
888    else if (int(version) == 2) {
889      return t_rnxObsFile::type3to2(sys, origType);
890    }
891    else if (int(version) == 3) {
892      return t_rnxObsFile::type2to3(sys, origType);
893    }
894  }
895  return "";
896}
897
898//
899////////////////////////////////////////////////////////////////////////////
900QStringList t_rnxObsHeader::phaseShifts() const {
901  QStringList strList;
902  QMap<QString, QPair<double, QStringList> >::const_iterator it =  _phaseShifts.begin();
903  while (it != _phaseShifts.end()) {
904    strList.append(QString("%1_%2:%3").arg(it.key(), 3).arg(it.value().first, 9, 'f', 3).arg(it.value().second.join("")));
905    it++;
906  }
907  return strList;
908}
909
910//
911////////////////////////////////////////////////////////////////////////////
912QStringList t_rnxObsHeader::gloBiases() const {
913  QStringList strList;
914  QMap<QString, double>::const_iterator it = _gloBiases.begin();
915  while (it != _gloBiases.end()) {
916    strList.append(QString("%1:%2").arg(it.key(), 3).arg(it.value(), 9, 'f', 3));
917    it++;
918  }
919  return strList;
920}
921
922//
923////////////////////////////////////////////////////////////////////////////
924QStringList t_rnxObsHeader::gloSlots() const {
925  QStringList strList;
926  QMap<t_prn, int>::const_iterator it = _gloSlots.begin();
927  while (it != _gloSlots.end()){
928    QString prn(it.key().toString().c_str());
929    strList.append(QString("%1:%2").arg(prn, 3).arg(it.value()));
930    it++;
931  }
932  return strList;
933}
934
935// Write Observation Types
936////////////////////////////////////////////////////////////////////////////
937QStringList t_rnxObsHeader::obsTypesStrings() const {
938
939  QStringList strList;
940  if (_version < 3.0) {
941    char sys0 = _usedSystems[0].toLatin1();
942    QString hlp;
943    QTextStream(&hlp) << QString("%1").arg(_obsTypes[sys0].size(), 6);
944    for (int ii = 0; ii < _obsTypes[sys0].size(); ii++) {
945      QTextStream(&hlp) << QString("%1").arg(_obsTypes[sys0][ii], 6);
946      if ((ii+1) % 9 == 0 || ii == _obsTypes[sys0].size()-1) {
947        strList.append(hlp.leftJustified(60) + "# / TYPES OF OBSERV\n");
948        hlp = QString().leftJustified(6);
949      }
950    }
951  }
952  else {
953    for (int iSys = 0; iSys < numSys(); iSys++) {
954      char sys = system(iSys);
955      QString hlp;
956      QTextStream(&hlp) << QString("%1  %2").arg(sys).arg(nTypes(sys), 3);
957      for (int iType = 0; iType < nTypes(sys); iType++) {
958        QString type = obsType(sys, iType);
959        QTextStream(&hlp) << QString(" %1").arg(type, -3);
960        if ((iType+1) % 13 == 0 || iType == nTypes(sys)-1) {
961          strList.append(hlp.leftJustified(60) + "SYS / # / OBS TYPES\n");
962          hlp = QString().leftJustified(6);
963        }
964      }
965    }
966  }
967
968  return strList;
969}
970
971// Constructor
972////////////////////////////////////////////////////////////////////////////
973t_rnxObsFile::t_rnxObsFile(const QString& fileName, e_inpOut inpOut) {
974  _inpOut       = inpOut;
975  _stream       = 0;
976  _flgPowerFail = false;
977  if (_inpOut == input) {
978    openRead(fileName);
979  }
980  else {
981    openWrite(fileName);
982  }
983}
984
985// Open for input
986////////////////////////////////////////////////////////////////////////////
987void t_rnxObsFile::openRead(const QString& fileName) {
988
989  _fileName = fileName; expandEnvVar(_fileName);
990  _file     = new QFile(_fileName);
991  _file->open(QIODevice::ReadOnly | QIODevice::Text);
992  _stream = new QTextStream();
993  _stream->setDevice(_file);
994
995  _header.read(_stream);
996
997  // Guess Observation Interval
998  // --------------------------
999  if (_header._interval == 0.0) {
1000    bncTime ttPrev;
1001    for (int iEpo = 0; iEpo < 10; iEpo++) {
1002      const t_rnxEpo* rnxEpo = nextEpoch();
1003      if (!rnxEpo) {
1004        throw QString("t_rnxObsFile: not enough epochs");
1005      }
1006      if (iEpo > 0) {
1007        double dt = rnxEpo->tt - ttPrev;
1008        if (_header._interval == 0.0 || dt < _header._interval) {
1009          _header._interval = dt;
1010        }
1011      }
1012      ttPrev = rnxEpo->tt;
1013    }
1014    _stream->seek(0);
1015    _header.read(_stream);
1016  }
1017
1018  // Time of first observation
1019  // -------------------------
1020  if (!_header._startTime.valid()) {
1021    const t_rnxEpo* rnxEpo = nextEpoch();
1022    if (!rnxEpo) {
1023      throw QString("t_rnxObsFile: not enough epochs");
1024    }
1025    _header._startTime = rnxEpo->tt;
1026    _stream->seek(0);
1027    _header.read(_stream);
1028  }
1029}
1030
1031// Open for output
1032////////////////////////////////////////////////////////////////////////////
1033void t_rnxObsFile::openWrite(const QString& fileName) {
1034
1035  _fileName = fileName; expandEnvVar(_fileName);
1036  _file     = new QFile(_fileName);
1037  _file->open(QIODevice::WriteOnly | QIODevice::Text);
1038  _stream = new QTextStream();
1039  _stream->setDevice(_file);
1040}
1041
1042// Destructor
1043////////////////////////////////////////////////////////////////////////////
1044t_rnxObsFile::~t_rnxObsFile() {
1045  close();
1046}
1047
1048// Close
1049////////////////////////////////////////////////////////////////////////////
1050void t_rnxObsFile::close() {
1051  delete _stream; _stream = 0;
1052  delete _file;   _file = 0;
1053}
1054
1055// Handle Special Epoch Flag
1056////////////////////////////////////////////////////////////////////////////
1057void t_rnxObsFile::handleEpochFlag(int flag, const QString& line,
1058                                   bool& headerReRead) {
1059
1060  headerReRead = false;
1061
1062  // Power Failure
1063  // -------------
1064  if      (flag == 1) {
1065    _flgPowerFail = true;
1066  }
1067
1068  // Start moving antenna
1069  // --------------------
1070  else if (flag == 2) {
1071    // no action
1072  }
1073
1074  // Re-Read Header
1075  // --------------
1076  else if (flag == 3 || flag == 4 || flag == 5) {
1077    int numLines = 0;
1078    if (version() < 3.0) {
1079      readInt(line, 29, 3, numLines);
1080    }
1081    else {
1082      readInt(line, 32, 3, numLines);
1083    }
1084    if (flag == 3 || flag == 4) {
1085      _header.read(_stream, numLines);
1086      headerReRead = true;
1087    }
1088    else {
1089      for (int ii = 0; ii < numLines; ii++) {
1090        _stream->readLine();
1091      }
1092    }
1093  }
1094
1095  // Unhandled Flag
1096  // --------------
1097  else {
1098    throw QString("t_rnxObsFile: unhandled flag\n" + line);
1099  }
1100}
1101
1102// Retrieve single Epoch
1103////////////////////////////////////////////////////////////////////////////
1104t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpoch() {
1105  _currEpo.clear();
1106  if (version() < 3.0) {
1107    return nextEpochV2();
1108  }
1109  else {
1110    return nextEpochV3();
1111  }
1112}
1113
1114// Retrieve single Epoch (RINEX Version 3)
1115////////////////////////////////////////////////////////////////////////////
1116t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV3() {
1117
1118  while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
1119
1120    QString line = _stream->readLine();
1121
1122    if (line.isEmpty()) {
1123      continue;
1124    }
1125
1126    int flag = 0;
1127    readInt(line, 31, 1, flag);
1128    if (flag > 0) {
1129      bool headerReRead = false;
1130      handleEpochFlag(flag, line, headerReRead);
1131      if (headerReRead) {
1132        continue;
1133      }
1134    }
1135
1136    QTextStream in(line.mid(1).toLatin1(), QIODevice::ReadOnly);
1137
1138    // Epoch Time
1139    // ----------
1140    int    year, month, day, hour, min;
1141    double sec;
1142    in >> year >> month >> day >> hour >> min >> sec;
1143    _currEpo.tt.set(year, month, day, hour, min, sec);
1144
1145    // Number of Satellites
1146    // --------------------
1147    int numSat;
1148    readInt(line, 32, 3, numSat);
1149
1150    _currEpo.rnxSat.resize(numSat);
1151
1152    // Observations
1153    // ------------
1154    for (int iSat = 0; iSat < numSat; iSat++) {
1155      line = _stream->readLine();
1156      t_prn prn; prn.set(line.left(3).toLatin1().data());
1157      _currEpo.rnxSat[iSat].prn = prn;
1158      char sys = prn.system();
1159      for (int iType = 0; iType < _header.nTypes(sys); iType++) {
1160        int pos = 3 + 16*iType;
1161        double obsValue = 0.0;
1162        int    lli      = 0;
1163        int    snr      = 0;
1164        readDbl(line, pos,     14, obsValue);
1165        readInt(line, pos + 14, 1, lli);
1166        readInt(line, pos + 15, 1, snr);
1167        if (_flgPowerFail) {
1168          lli |= 1;
1169        }
1170        QString type = obsType(sys, iType);
1171        _currEpo.rnxSat[iSat].obs[type].value = obsValue;
1172        _currEpo.rnxSat[iSat].obs[type].lli   = lli;
1173        _currEpo.rnxSat[iSat].obs[type].snr   = snr;
1174      }
1175    }
1176
1177    _flgPowerFail = false;
1178
1179    return &_currEpo;
1180  }
1181
1182  return 0;
1183}
1184
1185// Retrieve single Epoch (RINEX Version 2)
1186////////////////////////////////////////////////////////////////////////////
1187t_rnxObsFile::t_rnxEpo* t_rnxObsFile::nextEpochV2() {
1188
1189  while ( _stream->status() == QTextStream::Ok && !_stream->atEnd() ) {
1190
1191    QString line = _stream->readLine();
1192
1193    if (line.isEmpty()) {
1194      continue;
1195    }
1196
1197    int flag = 0;
1198    readInt(line, 28, 1, flag);
1199    if (flag > 0) {
1200      bool headerReRead = false;
1201      handleEpochFlag(flag, line, headerReRead);
1202      if (headerReRead) {
1203        continue;
1204      }
1205    }
1206
1207    QTextStream in(line.toLatin1(), QIODevice::ReadOnly);
1208
1209    // Epoch Time
1210    // ----------
1211    int    year, month, day, hour, min;
1212    double sec;
1213    in >> year >> month >> day >> hour >> min >> sec;
1214    if      (year <  80) {
1215      year += 2000;
1216    }
1217    else if (year < 100) {
1218      year += 1900;
1219    }
1220    _currEpo.tt.set(year, month, day, hour, min, sec);
1221
1222    // Number of Satellites
1223    // --------------------
1224    int numSat;
1225    readInt(line, 29, 3, numSat);
1226
1227    _currEpo.rnxSat.resize(numSat);
1228
1229    // Read Satellite Numbers
1230    // ----------------------
1231    int pos = 32;
1232    for (int iSat = 0; iSat < numSat; iSat++) {
1233      if (iSat > 0 && iSat % 12 == 0) {
1234        line = _stream->readLine();
1235        pos = 32;
1236      }
1237
1238      char sys = line.toLatin1()[pos];
1239      if (sys == ' ') {
1240        sys = 'G';
1241      }
1242      int satNum; readInt(line, pos + 1, 2, satNum);
1243      _currEpo.rnxSat[iSat].prn.set(sys, satNum);
1244
1245      pos += 3;
1246    }
1247
1248    // Read Observation Records
1249    // ------------------------
1250    for (int iSat = 0; iSat < numSat; iSat++) {
1251      char sys = _currEpo.rnxSat[iSat].prn.system();
1252      line = _stream->readLine();
1253      pos  = 0;
1254      for (int iType = 0; iType < _header.nTypes(sys); iType++) {
1255        if (iType > 0 && iType % 5 == 0) {
1256          line = _stream->readLine();
1257          pos  = 0;
1258        }
1259        double obsValue = 0.0;
1260        int    lli      = 0;
1261        int    snr      = 0;
1262        readDbl(line, pos,     14, obsValue);
1263        readInt(line, pos + 14, 1, lli);
1264        readInt(line, pos + 15, 1, snr);
1265
1266        if (_flgPowerFail) {
1267          lli |= 1;
1268        }
1269
1270        QString type = obsType(sys, iType);
1271        _currEpo.rnxSat[iSat].obs[type].value = obsValue;
1272        _currEpo.rnxSat[iSat].obs[type].lli   = lli;
1273        _currEpo.rnxSat[iSat].obs[type].snr   = snr;
1274
1275        pos += 16;
1276      }
1277    }
1278
1279    _flgPowerFail = false;
1280
1281    return &_currEpo;
1282  }
1283
1284  return 0;
1285}
1286
1287// Write Data Epoch
1288////////////////////////////////////////////////////////////////////////////
1289void t_rnxObsFile::writeEpoch(const t_rnxEpo* epo) {
1290  if (epo == 0) {
1291    return;
1292  }
1293  t_rnxEpo epoLocal;
1294  epoLocal.tt = epo->tt;
1295  for (unsigned ii = 0; ii < epo->rnxSat.size(); ii++) {
1296    const t_rnxSat& rnxSat = epo->rnxSat[ii];
1297    if (_header._obsTypes[rnxSat.prn.system()].size() > 0) {
1298      epoLocal.rnxSat.push_back(rnxSat);
1299    }
1300  }
1301
1302  if (version() < 3.0) {
1303    return writeEpochV2(_stream, _header, &epoLocal);
1304  }
1305  else {
1306    return writeEpochV3(_stream, _header, &epoLocal);
1307  }
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// Write Data Epoch (RINEX Version 3)
1406////////////////////////////////////////////////////////////////////////////
1407void t_rnxObsFile::writeEpochV3(QTextStream* stream, const t_rnxObsHeader& header,
1408                                const t_rnxEpo* epo) {
1409
1410  unsigned year, month, day, hour, min;
1411  double sec;
1412  epo->tt.civil_date(year, month, day);
1413  epo->tt.civil_time(hour, min, sec);
1414
1415  QString dateStr;
1416  QTextStream(&dateStr) << QString("> %1 %2 %3 %4 %5%6")
1417      .arg(year, 4)
1418      .arg(month, 2, 10, QChar('0'))
1419      .arg(day, 2, 10, QChar('0'))
1420      .arg(hour, 2, 10, QChar('0'))
1421      .arg(min, 2, 10, QChar('0'))
1422      .arg(sec, 11, 'f', 7);
1423
1424  int flag = 0;
1425  *stream << dateStr << QString("%1%2\n").arg(flag, 3).arg(epo->rnxSat.size(), 3);
1426
1427  for (unsigned iSat = 0; iSat < epo->rnxSat.size(); iSat++) {
1428    const t_rnxSat& rnxSat = epo->rnxSat[iSat];
1429    char sys = rnxSat.prn.system();
1430
1431    const t_rnxObs* hlp[header.nTypes(sys)];
1432    for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
1433      hlp[iTypeV3] = 0;
1434      QString typeV3 = header.obsType(sys, iTypeV3);
1435      QMapIterator<QString, t_rnxObs> itObs(rnxSat.obs);
1436
1437      // Exact match
1438      // -----------
1439      while (itObs.hasNext()) {
1440        itObs.next();
1441        const QString& type = itObs.key();
1442        const t_rnxObs& rnxObs = itObs.value();
1443        if (typeV3 == type2to3(sys, type) && rnxObs.value != 0.0) {
1444          hlp[iTypeV3] = &itObs.value();
1445        }
1446      }
1447
1448      // Non-Exact match
1449      // ---------------
1450      itObs.toFront();
1451      while (itObs.hasNext()) {
1452        itObs.next();
1453        const QString& type = itObs.key();
1454        const t_rnxObs& rnxObs = itObs.value();
1455        if (hlp[iTypeV3] == 0 && typeV3 == type2to3(sys, type).left(2) && rnxObs.value != 0.0) {
1456          hlp[iTypeV3] = &itObs.value();
1457        }
1458      }
1459    }
1460
1461    if (header.nTypes(sys)) {
1462      *stream << rnxSat.prn.toString().c_str();
1463      for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
1464        const t_rnxObs* rnxObs = hlp[iTypeV3];
1465        if (rnxObs == 0) {
1466          *stream << QString().leftJustified(16);
1467        }
1468        else {
1469          *stream << QString("%1").arg(rnxObs->value, 14, 'f', 3);
1470          if (rnxObs->lli != 0.0) {
1471            *stream << QString("%1").arg(rnxObs->lli, 1);
1472          }
1473          else {
1474            *stream << ' ';
1475          }
1476          if (rnxObs->snr != 0.0) {
1477            *stream << QString("%1").arg(rnxObs->snr, 1);
1478          }
1479          else {
1480            *stream << ' ';
1481          }
1482        }
1483      }
1484      *stream << endl;
1485    }
1486  }
1487}
1488
1489// Translate Observation Type v2 --> v3
1490////////////////////////////////////////////////////////////////////////////
1491QString t_rnxObsFile::type2to3(char sys, const QString& typeV2) {
1492  if      (typeV2 == "P1") {
1493    return (sys == 'G') ? "C1W" : "C1P";
1494  }
1495  else if (typeV2 == "P2") {
1496    return (sys == 'G') ? "C2W" : "C2P";
1497  }
1498  return typeV2;
1499}
1500
1501// Translate Observation Type v3 --> v2
1502////////////////////////////////////////////////////////////////////////////
1503QString t_rnxObsFile::type3to2(char /* sys */, const QString& typeV3) {
1504  if      (typeV3 == "C1P" || typeV3 == "C1W") {
1505    return "P1";
1506  }
1507  else if (typeV3 == "C2P" || typeV3 == "C2W") {
1508    return "P2";
1509  }
1510  return typeV3.left(2);
1511}
1512
1513// Set Observations from RINEX File
1514////////////////////////////////////////////////////////////////////////////
1515void t_rnxObsFile::setObsFromRnx(const t_rnxObsFile* rnxObsFile, const t_rnxObsFile::t_rnxEpo* epo,
1516                                 const t_rnxObsFile::t_rnxSat& rnxSat, t_satObs& obs) {
1517  obs._staID = rnxObsFile->markerName().toLatin1().constData();
1518  obs._prn   = rnxSat.prn;
1519  obs._time  = epo->tt;
1520
1521  char sys   = rnxSat.prn.system();
1522
1523  QChar addToL2;
1524  for (int iType = 0; iType < rnxObsFile->nTypes(sys); iType++) {
1525    QString type   = rnxObsFile->obsType(sys, iType);
1526    QString typeV3 = rnxObsFile->obsType(sys, iType, 3.0); // may or may not differ from type
1527    if (rnxSat.obs.contains(type) && rnxSat.obs[type].value != 0.0) {
1528      if (type == "P2" && typeV3.length() > 2) {
1529        addToL2 = typeV3[2];
1530        break;
1531      }
1532    }
1533  }
1534
1535  for (int iType = 0; iType < rnxObsFile->nTypes(sys); iType++) {
1536    QString type   = rnxObsFile->obsType(sys, iType);
1537    QString typeV3 = rnxObsFile->obsType(sys, iType, 3.0); // may or may not differ from type
1538    if (type == "L2") {
1539      typeV3 += addToL2;
1540    }
1541    if (rnxSat.obs.contains(type)) {
1542      const t_rnxObs& rnxObs = rnxSat.obs[type];
1543      if (rnxObs.value != 0.0) {
1544        string type2ch(typeV3.mid(1).toLatin1().data());
1545
1546        t_frqObs* frqObs = 0;
1547        for (unsigned iFrq = 0; iFrq < obs._obs.size(); iFrq++) {
1548          if (obs._obs[iFrq]->_rnxType2ch == type2ch) {
1549            frqObs = obs._obs[iFrq];
1550            break;
1551          }
1552        }
1553        if (frqObs == 0) {
1554          frqObs = new t_frqObs;
1555          frqObs->_rnxType2ch = type2ch;
1556          obs._obs.push_back(frqObs);
1557        }
1558
1559        switch( typeV3.toLatin1().data()[0] ) {
1560        case 'C':
1561          frqObs->_codeValid = true;
1562          frqObs->_code      = rnxObs.value;
1563          break;
1564        case 'L':
1565          frqObs->_phaseValid = true;
1566          frqObs->_phase      = rnxObs.value;
1567          frqObs->_slip       = (rnxObs.lli & 1);
1568          break;
1569        case 'D':
1570          frqObs->_dopplerValid = true;
1571          frqObs->_doppler      = rnxObs.value;
1572          break;
1573        case 'S':
1574          frqObs->_snrValid = true;
1575          frqObs->_snr      = rnxObs.value;
1576          break;
1577        }
1578
1579        // Handle old-fashioned SNR values
1580        // -------------------------------
1581        if (rnxObs.snr != 0 && !frqObs->_snrValid) {
1582          frqObs->_snrValid = true;
1583          frqObs->_snr      = rnxObs.snr * 6.0 + 2.5;
1584        }
1585      }
1586    }
1587  }
1588}
1589
1590// Tracking Mode Priorities
1591////////////////////////////////////////////////////////////////////////////
1592QStringList t_rnxObsFile::signalPriorities(char sys) {
1593
1594  bncSettings settings;
1595
1596  QStringList priorList;
1597  QString reqcAction = settings.value("reqcAction").toString();
1598
1599  // Priorities in Edit/Concatenate (post processing) mode
1600  // ---------------------------------------------------
1601  if (reqcAction == "Edit/Concatenate") {
1602    priorList = settings.value("reqcV2Priority").toString().split(" ", QString::SkipEmptyParts);
1603  }
1604
1605  // Priorities in real-time mode
1606  // ----------------------------
1607  else {
1608    priorList = settings.value("rnxV2Priority").toString().split(" ", QString::SkipEmptyParts);
1609  }
1610
1611  QStringList result;
1612  for (int ii = 0; ii < priorList.size(); ii++) {
1613    if (priorList[ii].indexOf(":") != -1) {
1614      QStringList hlp = priorList[ii].split(":", QString::SkipEmptyParts);
1615      if (hlp.size() == 2 && hlp[0].length() == 1 && hlp[0][0] == sys) {
1616        result.append(hlp[1]);
1617      }
1618    }
1619    else {
1620      result.append(priorList[ii]);
1621    }
1622  }
1623
1624  if (result.empty()) {
1625    switch (sys) {
1626      case 'G':
1627        result.append("12&PWCSLXYN");
1628        result.append("5&IQX");
1629        break;
1630      case 'R':
1631        result.append("12&PC");
1632        result.append("3&IQX");
1633        break;
1634      case 'E':
1635        result.append("16&BCX");
1636        result.append("578&IQX");
1637        break;
1638      case 'J':
1639        result.append("1&SLXCZ");
1640        result.append("26&SLX");
1641        result.append("5&IQX");
1642        break;
1643      case 'C':
1644        result.append("IQX");
1645        break;
1646      case 'I':
1647        result.append("ABCX");
1648        break;
1649      case 'S':
1650        result.append("1&C");
1651        result.append("5&IQX");
1652        break;
1653    }
1654  }
1655  return result;
1656}
Note: See TracBrowser for help on using the repository browser.