source: ntrip/trunk/BNC/src/bncrinex.cpp@ 7483

Last change on this file since 7483 was 7480, checked in by stuerze, 10 years ago

minor check to verify if there is really an downloaded skl file

File size: 17.2 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: bncRinex
30 *
31 * Purpose: writes RINEX files
32 *
33 * Author: L. Mervart
34 *
35 * Created: 27-Aug-2006
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <stdlib.h>
42#include <iostream>
43#include <iomanip>
44#include <math.h>
45#include <sstream>
46
47#include <QtCore>
48#include <QUrl>
49#include <QString>
50
51#include "bncrinex.h"
52#include "bnccore.h"
53#include "bncutils.h"
54#include "bncconst.h"
55#include "bnctabledlg.h"
56#include "bncgetthread.h"
57#include "bncnetqueryv1.h"
58#include "bncnetqueryv2.h"
59#include "bncsettings.h"
60#include "bncversion.h"
61
62using namespace std;
63
64// Constructor
65////////////////////////////////////////////////////////////////////////////
66bncRinex::bncRinex(const QByteArray& statID, const QUrl& mountPoint,
67 const QByteArray& latitude, const QByteArray& longitude,
68 const QByteArray& nmea, const QByteArray& ntripVersion) {
69
70 _statID = statID;
71 _mountPoint = mountPoint;
72 _latitude = latitude;
73 _longitude = longitude;
74 _nmea = nmea;
75 _ntripVersion = ntripVersion;
76 _headerWritten = false;
77 _reconnectFlag = false;
78
79 bncSettings settings;
80 _rnxScriptName = settings.value("rnxScript").toString();
81 expandEnvVar(_rnxScriptName);
82
83 _pgmName = QString(BNCPGMNAME).leftJustified(20, ' ', true);
84#ifdef WIN32
85 _userName = QString("${USERNAME}");
86#else
87 _userName = QString("${USER}");
88#endif
89 expandEnvVar(_userName);
90 _userName = _userName.leftJustified(20, ' ', true);
91
92 _samplingRate = settings.value("rnxSampl").toInt();
93
94 _writeRinexFileOnlyWithSkl = settings.value("rnxOnlyWithSKL").toBool();
95
96 _rnxV3filenames = settings.value("rnxV3filenames").toBool();
97}
98
99// Destructor
100////////////////////////////////////////////////////////////////////////////
101bncRinex::~bncRinex() {
102 bncSettings settings;
103 if ((_header.version() >= 3.0) && ( Qt::CheckState(settings.value("rnxAppend").toInt()) != Qt::Checked) ) {
104 _out << "> 4 1" << endl;
105 _out << "END OF FILE" << endl;
106 }
107 _out.close();
108}
109
110// Download Skeleton Header File
111////////////////////////////////////////////////////////////////////////////
112t_irc bncRinex::downloadSkeleton() {
113
114 t_irc irc = failure;
115
116 QStringList table;
117 bncTableDlg::getFullTable(_ntripVersion, _mountPoint.host(),
118 _mountPoint.port(), table, true);
119 QString net;
120 QStringListIterator it(table);
121 while (it.hasNext()) {
122 QString line = it.next();
123 if (line.indexOf("STR") == 0) {
124 QStringList tags = line.split(";");
125 if (tags.size() > 7) {
126 if (tags.at(1) == _mountPoint.path().mid(1).toAscii()) {
127 net = tags.at(7);
128 break;
129 }
130 }
131 }
132 }
133 QString sklDir;
134 if (!net.isEmpty()) {
135 it.toFront();
136 while (it.hasNext()) {
137 QString line = it.next();
138 if (line.indexOf("NET") == 0) {
139 QStringList tags = line.split(";");
140 if (tags.size() > 6) {
141 if (tags.at(1) == net) {
142 sklDir = tags.at(6).trimmed();
143 break;
144 }
145 }
146 }
147 }
148 }
149 if (!sklDir.isEmpty() && sklDir != "none") {
150 QUrl url(sklDir + "/" + _mountPoint.path().mid(1,4).toLower() + ".skl");
151 if (url.port() == -1) {
152 if (sklDir.contains("https", Qt::CaseInsensitive)) {
153 url.setPort(443);
154 }
155 else {
156 url.setPort(80);
157 }
158 }
159
160 bncNetQuery* query = new bncNetQueryV2(true);
161 QByteArray outData;
162 query->waitForRequestResult(url, outData);
163 if (query->status() == bncNetQuery::finished &&
164 outData.contains("END OF HEADER")) {
165 irc = success;
166 QTextStream in(outData);
167 _sklHeader.read(&in);
168 }
169
170 delete query;
171 }
172
173 return irc;
174}
175
176// Read Skeleton Header File
177////////////////////////////////////////////////////////////////////////////
178bool bncRinex::readSkeleton() {
179
180 bool readDone = false;
181
182 // Read the local file
183 // -------------------
184 QFile skl(_sklName);
185 if ( skl.exists() && skl.open(QIODevice::ReadOnly) ) {
186 readDone = true;
187 QTextStream in(&skl);
188 _sklHeader.read(&in);
189 }
190
191 // Read downloaded file
192 // --------------------
193 else if ( _ntripVersion != "N" && _ntripVersion != "UN" &&
194 _ntripVersion != "S" ) {
195 QDate currDate = currentDateAndTimeGPS().date();
196 if ( !_skeletonDate.isValid() || _skeletonDate != currDate ) {
197 if (downloadSkeleton() == success) {
198 readDone = true;
199 }
200 _skeletonDate = currDate;
201 } else if (_skeletonDate.isValid()) {
202 readDone = true;
203 }
204 }
205
206 return readDone;
207}
208
209// Next File Epoch (static)
210////////////////////////////////////////////////////////////////////////////
211QString bncRinex::nextEpochStr(const QDateTime& datTim,
212 const QString& intStr, bool rnxV3filenames,
213 QDateTime* nextEpoch) {
214
215 QString epoStr = "";
216
217 QTime nextTime;
218 QDate nextDate;
219
220 int indHlp = intStr.indexOf("min");
221
222 if ( indHlp != -1) {
223 int step = intStr.left(indHlp-1).toInt();
224 if (rnxV3filenames) {
225 epoStr += QString("%1").arg(datTim.time().hour(), 2, 10, QChar('0')); // H
226 } else {
227 epoStr += 'A' + datTim.time().hour();
228 }
229
230 if (datTim.time().minute() >= 60-step) {
231 epoStr += QString("%1").arg(60-step, 2, 10, QChar('0')); // M
232 if (datTim.time().hour() < 23) {
233 nextTime.setHMS(datTim.time().hour() + 1 , 0, 0);
234 nextDate = datTim.date();
235 }
236 else {
237 nextTime.setHMS(0, 0, 0);
238 nextDate = datTim.date().addDays(1);
239 }
240 }
241 else {
242 for (int limit = step; limit <= 60-step; limit += step) {
243 if (datTim.time().minute() < limit) {
244 epoStr += QString("%1").arg(limit-step, 2, 10, QChar('0')); // M
245 nextTime.setHMS(datTim.time().hour(), limit, 0);
246 nextDate = datTim.date();
247 break;
248 }
249 }
250 }
251 if (rnxV3filenames) {
252 epoStr += QString("%1").arg(0, 2, 10, QChar('0')); // S
253 epoStr += QString("_%1M").arg(step, 2, 10, QChar('0')); // period
254 }
255 }
256 else if (intStr == "1 hour") {
257 int step = intStr.left(indHlp-1).toInt();
258 if (rnxV3filenames) {
259 epoStr += QString("%1").arg(datTim.time().hour(), 2, 10, QChar('0')); // H
260 epoStr += QString("%1").arg(0, 2, 10, QChar('0')); // M
261 epoStr += QString("%1").arg(0, 2, 10, QChar('0')); // S
262 epoStr += QString("_%1H").arg(step+1, 2, 10, QChar('0')); // period
263 } else {
264 epoStr += 'A' + datTim.time().hour();
265 }
266 if (datTim.time().hour() < 23) {
267 nextTime.setHMS(datTim.time().hour() + 1 , 0, 0);
268 nextDate = datTim.date();
269 }
270 else {
271 nextTime.setHMS(0, 0, 0);
272 nextDate = datTim.date().addDays(1);
273 }
274 }
275 else {
276 int step = intStr.left(indHlp-1).toInt();
277 if (rnxV3filenames) {
278 epoStr += QString("%1").arg(0, 2, 10, QChar('0')); // H
279 epoStr += QString("%1").arg(0, 2, 10, QChar('0')); // M
280 epoStr += QString("%1").arg(0, 2, 10, QChar('0')); // S
281 epoStr += QString("_%1D").arg(step+1, 2, 10, QChar('0')); // period
282 } else {
283 epoStr = "0";
284 }
285 nextTime.setHMS(0, 0, 0);
286 nextDate = datTim.date().addDays(1);
287 }
288
289 if (nextEpoch) {
290 *nextEpoch = QDateTime(nextDate, nextTime);
291 }
292
293 return epoStr;
294}
295
296// File Name according to RINEX Standards
297////////////////////////////////////////////////////////////////////////////
298void bncRinex::resolveFileName(const QDateTime& datTim) {
299
300 bncSettings settings;
301 QString path = settings.value("rnxPath").toString();
302 expandEnvVar(path);
303
304 if ( path.length() > 0 && path[path.length()-1] != QDir::separator() ) {
305 path += QDir::separator();
306 }
307
308 QString hlpStr = nextEpochStr(datTim, settings.value("rnxIntr").toString(),
309 _rnxV3filenames, &_nextCloseEpoch);
310
311 QString ID4 = _statID.left(4);
312
313 // Check name conflict
314 // -------------------
315 QString distStr;
316 int num = 0;
317 QListIterator<QString> it(settings.value("mountPoints").toStringList());
318 while (it.hasNext()) {
319 QString mp = it.next();
320 if (mp.indexOf(ID4) != -1) {
321 ++num;
322 }
323 }
324 if (num > 1) {
325 distStr = "_" + _statID.mid(4);
326 }
327
328 QString sklExt = settings.value("rnxSkel").toString();
329 if (!sklExt.isEmpty()) {
330 _sklName = path + ID4 + distStr + "." + sklExt;
331 }
332
333 if (_rnxV3filenames) {
334 QString country;
335 QString monNum = "0";
336 QString recNum = "0";
337 QListIterator<QString> it(settings.value("mountPoints").toStringList());
338 while (it.hasNext()) {
339 QStringList hlp = it.next().split(" ");
340 if (hlp.size() < 7)
341 continue;
342 if (hlp.join(" ").indexOf(_statID, 0) != -1) {
343 country = hlp[2];
344 }
345 }
346 int sampl = settings.value("rnxSampl").toString().mid(0,2).toInt();
347 if (!sampl)
348 sampl++;
349 path += ID4 +
350 QString("%1").arg(monNum, 1, 10) +
351 QString("%1").arg(recNum, 1, 10) +
352 country +
353 "_S_" + // stream
354 QString("%1").arg(datTim.date().year()) +
355 QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
356 hlpStr + // HMS_period
357 QString("_%1S").arg(sampl, 2, 10, QChar('0')) + // sampling rate
358 distStr +
359 "_MO.rnx"; // mixed OBS
360 }
361 else {
362 path += ID4 +
363 QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
364 hlpStr + distStr + datTim.toString(".yyO");
365 }
366
367 _fName = path.toAscii();
368}
369
370// Write RINEX Header
371////////////////////////////////////////////////////////////////////////////
372void bncRinex::writeHeader(const QByteArray& format, const bncTime& firstObsTime) {
373
374 bncSettings settings;
375
376 // Set RINEX Version
377 // -----------------
378 int intHeaderVers = (Qt::CheckState(settings.value("rnxV3").toInt()) == Qt::Checked ? 3 : 2);
379
380 // Open the Output File
381 // --------------------
382 QDateTime datTimNom = dateAndTimeFromGPSweek(firstObsTime.gpsw(),
383 floor(firstObsTime.gpssec()+0.5));
384
385 resolveFileName(datTimNom);
386
387 // Read Skeleton Header
388 // --------------------
389 if (readSkeleton()) {
390 _header.set(_sklHeader, intHeaderVers);
391 }
392 else {
393 if (_writeRinexFileOnlyWithSkl) {
394 return;
395 }
396 _header.setDefault(_statID, intHeaderVers);
397 }
398
399 // Append to existing file and return
400 // ----------------------------------
401 if ( QFile::exists(_fName) &&
402 (_reconnectFlag || Qt::CheckState(settings.value("rnxAppend").toInt()) == Qt::Checked) ) {
403 _out.open(_fName.data(), ios::app);
404 _out.setf(ios::showpoint | ios::fixed);
405 _headerWritten = true;
406 _reconnectFlag = false;
407 }
408 else {
409 _out.open(_fName.data());
410 _addComments.clear();
411 }
412
413 _out.setf(ios::showpoint | ios::fixed);
414
415 // A Few Additional Comments
416 // -------------------------
417 _addComments << format.left(6) + " " + _mountPoint.host() + _mountPoint.path();
418 if (_nmea == "yes") {
419 _addComments << "NMEA LAT=" + _latitude + " " + "LONG=" + _longitude;
420 }
421
422 // Write the Header
423 // ----------------
424 QByteArray headerLines;
425 QTextStream outHlp(&headerLines);
426
427 QMap<QString, QString> txtMap;
428 txtMap["COMMENT"] = _addComments.join("\\n");
429
430 _header.setStartTime(firstObsTime);
431 _header.write(&outHlp, &txtMap);
432
433 outHlp.flush();
434
435 if (!_headerWritten) {
436 _out << headerLines.data();
437 }
438
439 _headerWritten = true;
440}
441
442// Stores Observation into Internal Array
443////////////////////////////////////////////////////////////////////////////
444void bncRinex::deepCopy(t_satObs obs) {
445 _obs.push_back(obs);
446}
447
448// Write One Epoch into the RINEX File
449////////////////////////////////////////////////////////////////////////////
450void bncRinex::dumpEpoch(const QByteArray& format, const bncTime& maxTime) {
451
452 // Select observations older than maxTime
453 // --------------------------------------
454 QList<t_satObs> obsList;
455 QMutableListIterator<t_satObs> mIt(_obs);
456 while (mIt.hasNext()) {
457 t_satObs obs = mIt.next();
458 if (obs._time < maxTime) {
459 obsList.push_back(obs);
460 mIt.remove();
461 }
462 }
463
464 // Easy Return
465 // -----------
466 if (obsList.isEmpty()) {
467 return;
468 }
469
470 // Time of Epoch
471 // -------------
472 const t_satObs& fObs = obsList.first();
473 QDateTime datTimNom = dateAndTimeFromGPSweek(fObs._time.gpsw(), floor(fObs._time.gpssec()+0.5));
474
475 // Close the file
476 // --------------
477 if (_nextCloseEpoch.isValid() && datTimNom >= _nextCloseEpoch) {
478 closeFile();
479 _headerWritten = false;
480 }
481
482 // Write RINEX Header
483 // ------------------
484 if (!_headerWritten) {
485 writeHeader(format, fObs._time);
486 }
487 if (!_headerWritten) {
488 return;
489 }
490
491 // Prepare structure t_rnxEpo
492 // --------------------------
493 t_rnxObsFile::t_rnxEpo rnxEpo;
494 rnxEpo.tt = fObs._time;
495
496 QListIterator<t_satObs> it(obsList);
497 while (it.hasNext()) {
498 const t_satObs& satObs = it.next();
499 t_rnxObsFile::t_rnxSat rnxSat;
500 rnxSat.prn = satObs._prn;
501
502 // Initialize all observations mentioned in skeleton header
503 // --------------------------------------------------------
504 char sys = rnxSat.prn.system();
505 for (int iType = 0; iType < _sklHeader.nTypes(sys); iType++) {
506 QString type = _sklHeader.obsType(sys, iType);
507 t_rnxObsFile::t_rnxObs rnxObs; // create an empty observation
508 rnxSat.obs[type] = rnxObs;
509 }
510
511 for (unsigned ii = 0; ii < satObs._obs.size(); ii++) {
512 const t_frqObs* frqObs = satObs._obs[ii];
513 if (frqObs->_codeValid) {
514 QString type = 'C' + QString(frqObs->_rnxType2ch.c_str());
515 t_rnxObsFile::t_rnxObs rnxObs;
516 rnxObs.value = frqObs->_code;
517 rnxSat.obs[type] = rnxObs;
518 }
519 if (frqObs->_phaseValid) {
520 QString type = 'L' + QString(frqObs->_rnxType2ch.c_str());
521 t_rnxObsFile::t_rnxObs rnxObs;
522 rnxObs.value = frqObs->_phase;
523 if (frqObs->_slip) {
524 rnxObs.lli |= 1;
525 }
526 rnxSat.obs[type] = rnxObs;
527 }
528 if (frqObs->_dopplerValid) {
529 QString type = 'D' + QString(frqObs->_rnxType2ch.c_str());
530 t_rnxObsFile::t_rnxObs rnxObs;
531 rnxObs.value = frqObs->_doppler;
532 rnxSat.obs[type] = rnxObs;
533 }
534 if (frqObs->_snrValid) {
535 QString type = 'S' + QString(frqObs->_rnxType2ch.c_str());
536 t_rnxObsFile::t_rnxObs rnxObs;
537 rnxObs.value = frqObs->_snr;
538 rnxSat.obs[type] = rnxObs;
539 }
540 }
541
542
543 rnxEpo.rnxSat.push_back(rnxSat);
544 }
545
546 // Write the epoch
547 // ---------------
548 QByteArray outLines;
549 QTextStream outStream(&outLines);
550 t_rnxObsFile::writeEpoch(&outStream, _header, &rnxEpo);
551
552 _out << outLines.data();
553 _out.flush();
554}
555
556// Close the Old RINEX File
557////////////////////////////////////////////////////////////////////////////
558void bncRinex::closeFile() {
559
560 if (_header.version() == 3) {
561 _out << "> 4 1" << endl;
562 _out << "END OF FILE" << endl;
563 }
564 _out.close();
565 if (!_rnxScriptName.isEmpty()) {
566 qApp->thread()->wait(100);
567#ifdef WIN32
568 QProcess::startDetached(_rnxScriptName, QStringList() << _fName) ;
569#else
570 QProcess::startDetached("nohup", QStringList() << _rnxScriptName << _fName) ;
571#endif
572
573 }
574}
575
576// One Line in ASCII (Internal) Format
577////////////////////////////////////////////////////////////////////////////
578string bncRinex::asciiSatLine(const t_satObs& obs) {
579
580 ostringstream str;
581 str.setf(ios::showpoint | ios::fixed);
582
583 str << obs._prn.toString() << ' ';
584
585 for (unsigned ii = 0; ii < obs._obs.size(); ii++) {
586 const t_frqObs* frqObs = obs._obs[ii];
587 if (frqObs->_codeValid) {
588 str << " C" << frqObs->_rnxType2ch << ' '
589 << setw(14) << setprecision(3) << frqObs->_code;
590 }
591 if (frqObs->_phaseValid) {
592 str << " L" << frqObs->_rnxType2ch << ' '
593 << setw(14) << setprecision(3) << frqObs->_phase
594 << ' ' << setw(3) << frqObs->_slipCounter;
595 }
596 if (frqObs->_dopplerValid) {
597 str << " D" << frqObs->_rnxType2ch << ' '
598 << setw(14) << setprecision(3) << frqObs->_doppler;
599 }
600 if (frqObs->_snrValid) {
601 str << " S" << frqObs->_rnxType2ch << ' '
602 << setw(8) << setprecision(3) << frqObs->_snr;
603 }
604 }
605
606 return str.str();
607}
Note: See TracBrowser for help on using the repository browser.