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

Last change on this file since 8656 was 8656, checked in by stuerze, 7 weeks ago

minor changes

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