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

Last change on this file since 7157 was 7150, checked in by stuerze, 10 years ago

skl file download is now always done using the ssl extended class to allow https requests; skl file download is independent from stream download and the chosen ntrip version

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