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

Last change on this file since 8642 was 8642, checked in by stuerze, 2 months ago

minor changes

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