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

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