Changeset 11200 in ntrip
- Timestamp:
- Nov 11, 2026, 11:17:08 AM (less than one hour ago)
- Location:
- trunk/BNC
- Files:
-
- 66 added
- 12 edited
-
CHANGELOG.md (modified) (1 diff)
-
src/bncrinex.cpp (modified) (1 diff)
-
src/rinex/reqcedit.cpp (modified) (10 diffs)
-
src/rinex/reqcedit.h (modified) (2 diffs)
-
src/rinex/rnxnavfile.cpp (modified) (10 diffs)
-
src/rinex/rnxnavfile.h (modified) (3 diffs)
-
src/rinex/rnxobsfile.cpp (modified) (2 diffs)
-
src/rinex/rnxobsfile.h (modified) (1 diff)
-
tests/README.md (modified) (2 diffs)
-
tests/cases/rinex_concat/expected/BRUX00BEL_S_20211251100_01H_01S_MO.rnx.gz (modified) ( previous)
-
tests/cases/rinex_concat_cli/expected/BRUX00BEL_S_20211251100_01H_01S_MO.rnx.gz (modified) ( previous)
-
tests/cases/rinex_concat_overlap (added)
-
tests/cases/rinex_concat_overlap/case.json (added)
-
tests/cases/rinex_concat_overlap/expected (added)
-
tests/cases/rinex_concat_overlap/expected/OBS_CONCAT_MO.rnx.gz (added)
-
tests/cases/rinex_concat_overlap/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_edit_long_marker (added)
-
tests/cases/rinex_edit_long_marker/case.json (added)
-
tests/cases/rinex_edit_long_marker/expected (added)
-
tests/cases/rinex_edit_long_marker/expected/LONG_MARKER_MO.rnx.gz (added)
-
tests/cases/rinex_edit_long_marker/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_edit_v402/expected/RNX402_MN.rnx.gz (modified) ( previous)
-
tests/cases/rinex_nav_concat_v2 (added)
-
tests/cases/rinex_nav_concat_v2/case.json (added)
-
tests/cases/rinex_nav_concat_v2/expected (added)
-
tests/cases/rinex_nav_concat_v2/expected/NAV211.26n.gz (added)
-
tests/cases/rinex_nav_concat_v2/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_nav_concat_v3 (added)
-
tests/cases/rinex_nav_concat_v3/case.json (added)
-
tests/cases/rinex_nav_concat_v3/expected (added)
-
tests/cases/rinex_nav_concat_v3/expected/NAV305_MN.rnx.gz (added)
-
tests/cases/rinex_nav_concat_v3/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_nav_concat_v3_to_v4 (added)
-
tests/cases/rinex_nav_concat_v3_to_v4/case.json (added)
-
tests/cases/rinex_nav_concat_v3_to_v4/expected (added)
-
tests/cases/rinex_nav_concat_v3_to_v4/expected/NAV305_V4_MN.rnx.gz (added)
-
tests/cases/rinex_nav_concat_v3_to_v4/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_nav_concat_v4 (added)
-
tests/cases/rinex_nav_concat_v4/case.json (added)
-
tests/cases/rinex_nav_concat_v4/expected (added)
-
tests/cases/rinex_nav_concat_v4/expected/BRUX_NAV_MN.rnx.gz (added)
-
tests/cases/rinex_nav_concat_v4/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_nav_concat_v4_to_v3 (added)
-
tests/cases/rinex_nav_concat_v4_to_v3/case.json (added)
-
tests/cases/rinex_nav_concat_v4_to_v3/expected (added)
-
tests/cases/rinex_nav_concat_v4_to_v3/expected/BRUX_NAV_V3_MN.rnx.gz (added)
-
tests/cases/rinex_nav_concat_v4_to_v3/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_nav_navic_irut_to_v3 (added)
-
tests/cases/rinex_nav_navic_irut_to_v3/case.json (added)
-
tests/cases/rinex_nav_navic_irut_to_v3/expected (added)
-
tests/cases/rinex_nav_navic_irut_to_v3/expected/NAVIC_V3_MN.rnx.gz (added)
-
tests/cases/rinex_nav_navic_irut_to_v3/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_nav_v2_to_v3 (added)
-
tests/cases/rinex_nav_v2_to_v3/case.json (added)
-
tests/cases/rinex_nav_v2_to_v3/expected (added)
-
tests/cases/rinex_nav_v2_to_v3/expected/NAV211_V3_MN.rnx.gz (added)
-
tests/cases/rinex_nav_v2_to_v3/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_nav_v4_gps_to_v2 (added)
-
tests/cases/rinex_nav_v4_gps_to_v2/case.json (added)
-
tests/cases/rinex_nav_v4_gps_to_v2/expected (added)
-
tests/cases/rinex_nav_v4_gps_to_v2/expected/NAV_GPS.25n.gz (added)
-
tests/cases/rinex_nav_v4_gps_to_v2/expected/RinexEdit.log.gz (added)
-
tests/cases/rinex_nav_v4_gps_to_v3 (added)
-
tests/cases/rinex_nav_v4_gps_to_v3/case.json (added)
-
tests/cases/rinex_nav_v4_gps_to_v3/expected (added)
-
tests/cases/rinex_nav_v4_gps_to_v3/expected/NAV_GPS_V3_MN.rnx.gz (added)
-
tests/cases/rinex_nav_v4_gps_to_v3/expected/RinexEdit.log.gz (added)
-
tests/data/nav211_1.26n (added)
-
tests/data/nav211_2.26n (added)
-
tests/data/nav305_1_MN.rnx (added)
-
tests/data/nav305_2_MN.rnx (added)
-
tests/data/nav401_gps_MN.rnx (added)
-
tests/data/nav402_navic_MN.rnx (added)
-
tests/data/nav402_p1_MN.rnx (added)
-
tests/data/nav402_p2_MN.rnx (added)
-
tests/data/nav402_p3_MN.rnx (added)
-
tests/data/obs_p1_MO.rnx (added)
-
tests/data/obs_p2_MO.rnx (added)
Legend:
- Unmodified
- Added
- Removed
-
trunk/BNC/CHANGELOG.md
r11196 r11200 1 1 # Changelog 2 2 ## 2.13.8 (2026-11-04) 3 - FIXED: RINEX 3/4 observation files: MARKER NAME was cut to 9 characters (A60 in RINEX 3/4); RINEX 2 keeps the 4-character station ID, the marker name derived from the stream name in real-time RINEX files stays 9 characters (e.g. WTZR00DEU) 4 - CHANGED: RINEX 4 -> 3 navigation conversion: of the NavIC IRUT offsets the one to UTCIRN is written (RINEX 3 has no UTC identifier for UTC(NPLI)) 5 - ADDED: **RINEX navigation conversion of the ionosphere and time system parameters**: RINEX 4 ION/STO records are written as RINEX 3 IONOSPHERIC CORR (GPS/QZS/BDS/IRN Klobuchar, Galileo NeQuick-G; BDS with time mark and SV, GEO/IGSO preferred) and TIME SYSTEM CORR (T, W from t_ot, SV, UTC identifier) resp. RINEX 2 ION ALPHA/ION BETA/DELTA-UTC and CORR TO SYSTEM TIME (first record of each kind); RINEX 2 <-> 3 header records are converted for GPS and GLONASS. Records without RINEX 3/2 equivalent (EOP, BDGIM, NavIC L1NV, STO with A2 or of other systems) are reported in the log; RINEX 2/3 header records cannot become RINEX 4 records (no transmission time and satellite) and are reported as well 6 - FIXED: **RINEX concatenation of overlapping observation files: epochs were written twice** (time going back at the file boundary); now an epoch is written only if later than the last one written (first file wins), the number skipped is logged 7 - ADDED: RINEX concatenation: changes of the station header records between the observation files (e.g. receiver, antenna, marker, eccentricities, after the header editing options) are written as event flag 4 before the first epoch of the next file; before, only the header of the first file was used 8 - ADDED: **RINEX navigation concatenation keeps the RINEX 4 STO, EOP and ION records** (before always skipped): duplicates removed, records in the time window written in time order with the ephemerides; when writing RINEX 3 or 2 the number of records not written is logged 9 - ADDED: RINEX navigation concatenation keeps the header records of input files of the same RINEX version (e.g. IONOSPHERIC CORR, TIME SYSTEM CORR, ION ALPHA, ION BETA, DELTA-UTC: A0,A1,T,W, DOI; the first one of a kind, IONOSPHERIC CORR/TIME SYSTEM CORR per correction type) and LEAP SECONDS of the input (incl. future leap seconds) in RINEX 3 and 4; records of other versions are reported in the log as not converted 10 - FIXED: RINEX navigation concatenation: MERGED FILE written with 19 instead of 9 digits, also for a single file and with the number of file name patterns instead of files (input files already merged now count with their MERGED FILE value); comments and PGM / RUN BY / DATE records of input files given with wildcards were lost 3 11 - ADDED: **PPP post-fit residual RMS**: RMS of the code and of the phase post-fit residuals per epoch (observations used in the final filter update, outliers removed), written to the PPP logfile ('RMS code ... (n) phase ... (n)' after the residuals) and as fields 'res_rms_code' and 'res_rms_phase' of the structured 'ppp_position' record 4 12 - FIXED: PPP ambiguity resolution: ambiguities of satellites not observed in the current epoch are left out of the search (listed as 'not observed' in the PPP logfile); before, an ambiguity parameter kept up to 60 s after the last observation (e.g. satellite set below the minimum elevation) was still searched and fixed with its last float value -
trunk/BNC/src/bncrinex.cpp
r11057 r11200 475 475 return; 476 476 } 477 _header.setDefault(_statID, intHeaderVers); 477 // marker name from the stream name, e.g. WTZR00DEU0 -> WTZR00DEU 478 _header.setDefault(_statID.left(9), intHeaderVers); 478 479 } 479 480 -
trunk/BNC/src/rinex/reqcedit.cpp
r11192 r11200 327 327 // Loop over all input observation files 328 328 // ------------------------------------- 329 bncTime lastWritten; // epochs of overlapping files are written once 330 int numDuplicates = 0; 329 331 for (int ii = 0; ii < _rnxObsFiles.size(); ii++) { 330 332 t_rnxObsFile* obsFile = _rnxObsFiles[ii]; … … 334 336 << obsFile->startTime().timestr(0).c_str() << Qt::endl; 335 337 } 338 // header records changed with respect to the previous file (after the 339 // header editing options): event with header information (flag 4) 340 editRnxObsHeader(*obsFile); 341 QList<t_rnxObsFile::t_rnxEvent> headerChange; 342 if (ii > 0) { 343 QStringList records = headerChangeRecords(_rnxObsFiles[ii-1]->header(), 344 obsFile->header(), 345 outObsFile.header().version()); 346 if (!records.isEmpty()) { 347 t_rnxObsFile::t_rnxEvent event; 348 event.flag = 4; 349 event.version = outObsFile.header().version(); 350 event.records = records; 351 headerChange.append(event); 352 if (_logStream) { 353 *_logStream << "Header changes : " << records.size() << " records (" 354 << obsFile->fileName() << ")" << Qt::endl; 355 } 356 } 357 } 336 358 if (ii == 0) { 337 359 outObsFile.setHeader(obsFile->header(), int(_rnxVersion), &useObsTypes, … … 365 387 // sampling) are written with the next epoch; events with an epoch 366 388 // outside the time window are skipped 367 QList<t_rnxObsFile::t_rnxEvent> pendingEvents; 389 QList<t_rnxObsFile::t_rnxEvent> pendingEvents = headerChange; 368 390 bool pendingPowerFail = false; 369 391 bool endReached = false; … … 379 401 endReached = true; 380 402 break; 403 } 404 if (lastWritten.valid() && !(epo->tt > lastWritten)) { 405 ++numDuplicates; // already written from the previous file 406 continue; 381 407 } 382 408 … … 396 422 } 397 423 outObsFile.writeEpoch(epo); 424 lastWritten = epo->tt; 398 425 } 399 426 else { … … 426 453 } 427 454 } 455 if (_logStream && numDuplicates) { 456 *_logStream << "Epochs skipped : " << numDuplicates 457 << " (not later than the last epoch written, e.g. overlapping files)" << Qt::endl; 458 } 459 } 460 461 // Station header records of header2 differing from header1 (e.g. new 462 // receiver or antenna in the next file), in the output RINEX version 463 //////////////////////////////////////////////////////////////////////////// 464 QStringList t_reqcEdit::headerChangeRecords(const t_rnxObsHeader& header1, 465 const t_rnxObsHeader& header2, 466 double outVersion) { 467 QMap<QString, QStringList> records1, records2; 468 QStringList labels; 469 for (int iHdr = 0; iHdr < 2; iHdr++) { 470 const t_rnxObsHeader& header = (iHdr == 0) ? header1 : header2; 471 QMap<QString, QStringList>& records = (iHdr == 0) ? records1 : records2; 472 QStringList lines = header.stationRecords(outVersion); 473 QStringList other = header.otherRecords(); 474 for (int ii = 0; ii < other.size(); ii++) { 475 QString record = t_rnxObsHeader::convertRecord(other[ii], outVersion, 476 header.otherRecordsVersion()); 477 if (!record.isEmpty()) { 478 lines << record; 479 } 480 } 481 if (header.rcvClkOffsAppl() < 0) { // not given: default 0 482 lines << QString("%1").arg(0, 6).leftJustified(60) + "RCV CLOCK OFFS APPL"; 483 } 484 for (int ii = 0; ii < lines.size(); ii++) { 485 QString label = lines[ii].mid(60).trimmed(); 486 records[label] << lines[ii]; 487 if (iHdr == 1 && !labels.contains(label)) { 488 labels << label; 489 } 490 } 491 } 492 QStringList changed; 493 for (int ii = 0; ii < labels.size(); ii++) { 494 if (records1.value(labels[ii]) != records2[labels[ii]]) { 495 changed << records2[labels[ii]]; 496 } 497 } 498 return changed; 428 499 } 429 500 … … 709 780 comments.append(comment); 710 781 } 711 QStringListIterator it(_navFileNames); 782 // (number of merged files: input files already merged count with their 783 // MERGED FILE value) 784 int mergedNavFiles = 0; 785 QStringList navFiles = expandFileNames(_navFileNames); 786 QStringListIterator it(navFiles); 712 787 while (it.hasNext()) { 713 788 QString fileName = it.next(); 714 789 715 790 t_rnxNavFile rnxNavFile(fileName, t_rnxNavFile::input); 791 mergedNavFiles += qMax(1, rnxNavFile.mergedFiles()); 716 792 QStringListIterator itCmnt(rnxNavFile.comments()); 717 793 while (itCmnt.hasNext()) { … … 813 889 } 814 890 815 int mergedNavFiles = _navFileNames.size(); 891 // Header records and RINEX 4 STO/EOP/ION records of all input files: 892 // header records of the same RINEX version (the first one of a kind), 893 // records without duplicates, in the time window 894 // --------------------------------------------------------------------- 895 QStringList headerRecords; 896 QList<t_rnxNavFile::t_rnxNavRecord> records; 897 collectNavRecords(outNavFile, headerRecords, records); 898 816 899 unsigned year, month, day; 817 900 int gps_utc = 0; … … 820 903 gps_utc = gnumleap(year, month, day); 821 904 } 822 outNavFile.writeHeader(&txtMap, mergedNavFiles, gps_utc); 905 outNavFile.writeHeader(&txtMap, mergedNavFiles, gps_utc, &headerRecords); 906 907 // records without epoch first, then in time order with the ephemerides 908 int iRec = 0; 909 while (iRec < records.size() && !records[iRec].tt.valid()) { 910 outNavFile.writeRecord(records[iRec++]); 911 } 823 912 824 913 // Loop over all ephemerides … … 850 939 } 851 940 } 941 while (iRec < records.size() && records[iRec].tt <= eph->TOC()) { 942 outNavFile.writeRecord(records[iRec++]); 943 } 852 944 outNavFile.writeEph(eph); 945 } 946 while (iRec < records.size()) { 947 outNavFile.writeRecord(records[iRec++]); 948 } 949 } 950 951 // File names with wildcards expanded (as in readEphemerides) 952 //////////////////////////////////////////////////////////////////////////// 953 QStringList t_reqcEdit::expandFileNames(const QStringList& fileNames) { 954 QStringList expanded; 955 QStringListIterator itNames(fileNames); 956 while (itNames.hasNext()) { 957 QString fileName = itNames.next(); 958 if (fileName.indexOf('*') != -1 || fileName.indexOf('?') != -1) { 959 QFileInfo fileInfo(fileName); 960 QDir dir = fileInfo.dir(); 961 QStringList filters; filters << fileInfo.fileName(); 962 QListIterator<QFileInfo> it(dir.entryInfoList(filters)); 963 while (it.hasNext()) { 964 expanded << it.next().filePath(); 965 } 966 } 967 else { 968 expanded << fileName; 969 } 970 } 971 return expanded; 972 } 973 974 // Key of a navigation header record: one record per kind is written (the 975 // first one), IONOSPHERIC CORR and TIME SYSTEM CORR per correction type 976 //////////////////////////////////////////////////////////////////////////// 977 static QString navHeaderKey(const QString& record) { 978 QString label = record.mid(60).trimmed(); 979 if (label == "IONOSPHERIC CORR") { 980 return label + record.left(4) + record.mid(54, 6); // type, time mark, SV 981 } 982 if (label == "TIME SYSTEM CORR") { 983 return label + record.left(4); 984 } 985 static const QStringList single = QStringList() 986 << "LEAP SECONDS" << "ION ALPHA" << "ION BETA" << "DELTA-UTC: A0,A1,T,W" 987 << "CORR TO SYSTEM TIME" << "D-UTC A0,A1,T,W,S,U" << "DOI"; 988 if (single.contains(label)) { 989 return label; 990 } 991 return record; // e.g. LICENSE OF USE, STATION INFORMATION: identical ones once 992 } 993 994 // Conversion of navigation header records between RINEX versions and of 995 // RINEX 4 STO/ION records to RINEX 3/2 header records (RINEX 3.05 Table A5, 996 // RINEX 2.11, RINEX 4.02 Tables 26-28, A33-A37: same sign conventions) 997 //////////////////////////////////////////////////////////////////////////// 998 namespace { 999 1000 double navField(const QString& line, int pos, int len = 19) { 1001 double value = 0.0; 1002 readDbl(line, pos, len, value); 1003 return value; 1004 } 1005 1006 bool navEpoch(const QString& line, bncTime& tt) { 1007 QTextStream in(line.mid(4, 19).toLatin1(), QIODevice::ReadOnly); 1008 int year = 0, month = 0, day = 0, hour = 0, min = 0; 1009 double sec = 0.0; 1010 in >> year >> month >> day >> hour >> min >> sec; 1011 if (year <= 0) { 1012 return false; 1013 } 1014 tt.set(year, month, day, hour, min, sec); 1015 return true; 1016 } 1017 1018 QString navRecord(const QString& value, const QString& label) { 1019 return value.leftJustified(60) + label; 1020 } 1021 1022 // RINEX 3 IONOSPHERIC CORR: A4,1X,4D12.4,1X,A1,1X,I2 1023 QString ionCorrV3(const QString& type, const double par[4], QChar timeMark, int sv) { 1024 QString value = type.leftJustified(4) + " "; 1025 for (int ii = 0; ii < 4; ii++) { 1026 value += QString("%1").arg(par[ii], 12, 'E', 4); 1027 } 1028 value += QString(" %1 ").arg(timeMark); 1029 value += (sv > 0) ? QString("%1").arg(sv, 2) : QString(" "); 1030 return navRecord(value, "IONOSPHERIC CORR"); 1031 } 1032 1033 // RINEX 3 TIME SYSTEM CORR: A4,1X,D17.10,D16.9,1X,I6,1X,I4,1X,A5,1X,I2 1034 QString timeCorrV3(const QString& type, double a0, double a1, int tt, int week, 1035 const QString& svId, int utcId) { 1036 QString value = type.leftJustified(4) + " " 1037 + QString("%1").arg(a0, 17, 'E', 10) + QString("%1").arg(a1, 16, 'E', 9) 1038 + QString(" %1 %2 %3 %4").arg(tt, 6).arg(week, 4).arg(svId, 5).arg(utcId, 2); 1039 return navRecord(value, "TIME SYSTEM CORR"); 1040 } 1041 1042 // RINEX 2 ION ALPHA/ION BETA: 2X,4D12.4 1043 QString ionV2(const QString& label, const double par[4]) { 1044 QString value = " "; 1045 for (int ii = 0; ii < 4; ii++) { 1046 value += QString("%1").arg(par[ii], 12, 'E', 4); 1047 } 1048 return navRecord(value, label); 1049 } 1050 1051 // RINEX 2 DELTA-UTC: A0,A1,T,W: 3X,2D19.12,2I9 1052 QString deltaUtcV2(double a0, double a1, int tt, int week) { 1053 QString value = " " + QString("%1").arg(a0, 19, 'E', 12) + QString("%1").arg(a1, 19, 'E', 12) 1054 + QString("%1%2").arg(tt, 9).arg(week, 9); 1055 return navRecord(value, "DELTA-UTC: A0,A1,T,W"); 1056 } 1057 1058 // RINEX 2 CORR TO SYSTEM TIME: 3I6,3X,D19.12 (-TauC) 1059 QString corrSysTimeV2(int year, int month, int day, double minusTauC) { 1060 QString value = QString("%1%2%3 ").arg(year, 6).arg(month, 6).arg(day, 6) 1061 + QString("%1").arg(minusTauC, 19, 'E', 12); 1062 return navRecord(value, "CORR TO SYSTEM TIME"); 1063 } 1064 1065 // UTC identifier of RINEX 3 TIME SYSTEM CORR from the RINEX 4 UTC ID 1066 int utcIdV3(const QString& utcId) { 1067 if (utcId == "UTC(NIST)") return 1; 1068 if (utcId == "UTC(USNO)") return 2; 1069 if (utcId == "UTC(SU)") return 3; 1070 if (utcId == "UTC(BIPM)") return 4; 1071 if (utcId == "UTCGAL") return 5; // UTC(Europe Lab) 1072 if (utcId == "UTC(CRL)" || utcId == "UTC(NICT)") return 6; 1073 if (utcId == "UTC(NTSC)") return 7; 1074 return 0; 1075 } 1076 1077 // Priority of BDS satellites for the ionosphere parameters (RINEX 3.05 1078 // Table A5 Note 2): GEO, IGSO, MEO 1079 int bdsPriority(int prn) { 1080 if (prn <= 5 || (prn >= 59 && prn <= 62)) { 1081 return 0; 1082 } 1083 if ((prn >= 6 && prn <= 10) || prn == 13 || prn == 16 || prn == 31 || 1084 (prn >= 38 && prn <= 40) || prn == 56) { 1085 return 1; 1086 } 1087 return 2; 1088 } 1089 1090 } // namespace 1091 1092 // RINEX 4 STO/ION records -> RINEX 3 or 2 header records (one per kind: 1093 // the first record in time, BDS by satellite priority); returns the number 1094 // of records not convertible 1095 //////////////////////////////////////////////////////////////////////////// 1096 int t_reqcEdit::navRecordsToHeader(const QList<t_rnxNavFile::t_rnxNavRecord>& records, 1097 double outVersion, bool outGlonass, 1098 QStringList& headerRecords, QStringList& notConverted) { 1099 QMap<QString, QPair<int, QString> > converted; // key -> (priority, record) 1100 int numNotConverted = 0; 1101 1102 for (int iRec = 0; iRec < records.size(); iRec++) { 1103 const t_rnxNavFile::t_rnxNavRecord& rec = records[iRec]; 1104 QStringList hlp = rec.lines[0].split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); 1105 QString prnStr = hlp.value(2); 1106 QString navType = hlp.value(3); 1107 QString subType = hlp.value(4); 1108 char sys = prnStr.isEmpty() ? ' ' : prnStr[0].toLatin1(); 1109 int prnNum = prnStr.mid(1).toInt(); 1110 QString key; 1111 QString record; 1112 int priority = 0; 1113 1114 // (rec.lines[0] is the "> " line, the data lines follow) 1115 if (rec.key == "ION" && rec.lines.size() >= 4 && subType.isEmpty() && 1116 ((sys == 'G' && (navType == "LNAV" || navType == "CNVX")) || 1117 (sys == 'J' && navType == "LNAV") || 1118 (sys == 'C' && navType == "D1D2") || 1119 (sys == 'I' && navType == "LNAV"))) { 1120 // Klobuchar 1121 double alpha[4], beta[4]; 1122 alpha[0] = navField(rec.lines[1], 23); 1123 alpha[1] = navField(rec.lines[1], 42); 1124 alpha[2] = navField(rec.lines[1], 61); 1125 alpha[3] = navField(rec.lines[2], 4); 1126 beta[0] = navField(rec.lines[2], 23); 1127 beta[1] = navField(rec.lines[2], 42); 1128 beta[2] = navField(rec.lines[2], 61); 1129 beta[3] = navField(rec.lines[3], 4); 1130 priority = (navType == "CNVX") ? 1 : 0; // LNAV preferred 1131 if (outVersion >= 3.0) { 1132 QString prefix = (sys == 'G') ? "GPS" : (sys == 'J') ? "QZS" : (sys == 'C') ? "BDS" : "IRN"; 1133 QChar timeMark = ' '; 1134 int sv = 0; 1135 if (sys == 'C') { // mandatory for BDS: hour of the transmission time, SV 1136 bncTime tt; 1137 if (navEpoch(rec.lines[1], tt)) { 1138 unsigned hour, min; double sec; 1139 tt.civil_time(hour, min, sec); 1140 timeMark = QChar('A' + int(hour)); 1141 } 1142 sv = prnNum; 1143 priority = bdsPriority(prnNum); 1144 } 1145 key = prefix; 1146 record = ionCorrV3(prefix + "A", alpha, timeMark, sv) + "\n" + 1147 ionCorrV3(prefix + "B", beta, timeMark, sv); 1148 } 1149 else if (sys == 'G' && !outGlonass) { 1150 key = "ION"; 1151 record = ionV2("ION ALPHA", alpha) + "\n" + ionV2("ION BETA", beta); 1152 } 1153 } 1154 else if (rec.key == "ION" && rec.lines.size() >= 2 && sys == 'E' && navType == "IFNV" && 1155 outVersion >= 3.0) { 1156 // NeQuick-G 1157 double ai[4]; 1158 ai[0] = navField(rec.lines[1], 23); 1159 ai[1] = navField(rec.lines[1], 42); 1160 ai[2] = navField(rec.lines[1], 61); 1161 ai[3] = 0.0; 1162 key = "GAL"; 1163 record = ionCorrV3("GAL", ai, ' ', 0); 1164 record = record.left(5 + 36) + QString().leftJustified(12) + record.mid(5 + 48); // ai3 blank 1165 } 1166 else if (rec.key == "STO" && rec.lines.size() >= 3) { 1167 QString type = rec.lines[1].mid(24, 18).trimmed(); 1168 QString sbas = rec.lines[1].mid(43, 18).trimmed(); 1169 QString utcId = rec.lines[1].mid(62, 18).trimmed(); 1170 double a0 = navField(rec.lines[2], 23); 1171 double a1 = navField(rec.lines[2], 42); 1172 double a2 = navField(rec.lines[2], 61); 1173 bncTime tot; 1174 static const QStringList v3Types = QStringList() 1175 << "GPUT" << "GLUT" << "GAUT" << "BDUT" << "QZUT" << "IRUT" << "SBUT" 1176 << "GLGP" << "GAGP" << "QZGP" << "IRGP"; 1177 if (a2 == 0.0 && navEpoch(rec.lines[1], tot) && v3Types.contains(type)) { 1178 int week = 0, sow = 0; 1179 if (!type.startsWith("GL")) { 1180 week = int(tot.gpsw()); 1181 sow = int(nint(tot.gpssec())); 1182 if (type.startsWith("BD")) { 1183 week -= 1356; // BDS week 1184 } 1185 } 1186 // one record per offset type (RINEX 3: no UTC ID for UTC(NPLI)); 1187 // NavIC: UTCIRN preferred to UTC(NPLI), SBAS: per SBAS ID 1188 key = type + (type == "SBUT" ? sbas : QString()); 1189 if (type == "IRUT" && utcId != "UTCIRN") { 1190 priority = 1; 1191 } 1192 if (outVersion >= 3.0) { 1193 QString svId = (type == "SBUT" && !sbas.isEmpty()) ? sbas : prnStr; 1194 record = timeCorrV3(type, a0, a1, sow, week, svId.length() > 1 ? svId : QString(), 1195 utcIdV3(utcId)); 1196 } 1197 else if (type == "GPUT" && !outGlonass) { 1198 key = "GPUT"; 1199 record = deltaUtcV2(a0, a1, sow, week); 1200 } 1201 else if (type == "GLUT" && outGlonass) { 1202 unsigned year, month, day; 1203 tot.civil_date(year, month, day); 1204 key = "GLUT"; 1205 record = corrSysTimeV2(int(year), int(month), int(day), a0); 1206 } 1207 } 1208 } 1209 1210 if (record.isEmpty()) { 1211 // not convertible to the output version (e.g. EOP, BDGIM, NavIC 1212 // L1NV, STO with A2, STO types not in RINEX 3) or not used in a 1213 // RINEX 2 file of another system 1214 bool usable = !(outVersion < 3.0 && 1215 ((outGlonass && sys != 'R') || (!outGlonass && sys != 'G'))); 1216 if (usable) { 1217 ++numNotConverted; 1218 QString what = rec.key + " " + QString(sys) + " " + navType 1219 + (subType.isEmpty() ? QString() : " " + subType); 1220 if (!notConverted.contains(what)) { 1221 notConverted << what; 1222 } 1223 } 1224 continue; 1225 } 1226 if (!converted.contains(key) || priority < converted[key].first) { 1227 converted[key] = QPair<int, QString>(priority, record); 1228 } 1229 } 1230 1231 QMapIterator<QString, QPair<int, QString> > it(converted); 1232 while (it.hasNext()) { 1233 it.next(); 1234 headerRecords << it.value().second.split("\n"); 1235 } 1236 return numNotConverted; 1237 } 1238 1239 // Navigation header record converted between RINEX 2 and 3 (GPS ionosphere 1240 // and UTC parameters, GLONASS time correction); empty list: not convertible 1241 //////////////////////////////////////////////////////////////////////////// 1242 QStringList t_reqcEdit::convertNavHeaderRecord(const QString& record, double inVersion, 1243 double outVersion, bool outGlonass) { 1244 QStringList result; 1245 QString label = record.mid(60).trimmed(); 1246 if (int(inVersion) == 2 && int(outVersion) == 3) { 1247 if (label == "ION ALPHA" || label == "ION BETA") { 1248 double par[4]; 1249 for (int ii = 0; ii < 4; ii++) { 1250 par[ii] = navField(record, 2 + 12*ii, 12); 1251 } 1252 result << ionCorrV3(label == "ION ALPHA" ? "GPSA" : "GPSB", par, ' ', 0); 1253 } 1254 else if (label == "DELTA-UTC: A0,A1,T,W") { 1255 int tt = 0, week = 0; 1256 readInt(record, 41, 9, tt); 1257 readInt(record, 50, 9, week); 1258 result << timeCorrV3("GPUT", navField(record, 3), navField(record, 22), 1259 tt, week, QString(), 2); // GPS: UTC(USNO) 1260 } 1261 else if (label == "CORR TO SYSTEM TIME") { 1262 result << timeCorrV3("GLUT", navField(record, 21), 0.0, 0, 0, QString(), 3); // UTC(SU) 1263 } 1264 } 1265 else if (int(inVersion) == 3 && int(outVersion) == 2 && !outGlonass) { 1266 QString type = record.left(4); 1267 if (label == "IONOSPHERIC CORR" && (type == "GPSA" || type == "GPSB")) { 1268 double par[4]; 1269 for (int ii = 0; ii < 4; ii++) { 1270 par[ii] = navField(record, 5 + 12*ii, 12); 1271 } 1272 result << ionV2(type == "GPSA" ? "ION ALPHA" : "ION BETA", par); 1273 } 1274 else if (label == "TIME SYSTEM CORR" && type == "GPUT") { 1275 int tt = 0, week = 0; 1276 readInt(record, 39, 6, tt); 1277 readInt(record, 46, 4, week); 1278 result << deltaUtcV2(navField(record, 5, 17), navField(record, 22, 16), tt, week); 1279 } 1280 } 1281 return result; 1282 } 1283 1284 // Header records and STO/EOP/ION records of the input navigation files 1285 //////////////////////////////////////////////////////////////////////////// 1286 void t_reqcEdit::collectNavRecords(const t_rnxNavFile& outNavFile, 1287 QStringList& headerRecords, 1288 QList<t_rnxNavFile::t_rnxNavRecord>& records) { 1289 QSet<QString> headerKeys; 1290 QSet<QString> recordKeys; 1291 int numNotConverted = 0; 1292 int numRecordsLost = 0; 1293 QStringList notConvertedLabels; 1294 1295 QStringList navFiles = expandFileNames(_navFileNames); 1296 1297 for (int iFile = 0; iFile < navFiles.size(); iFile++) { 1298 t_rnxNavFile navFile(navFiles[iFile], t_rnxNavFile::input); 1299 1300 // header records: same RINEX version (RINEX 2: same file type) 1301 bool sameVersion = (int(navFile.version()) == int(outNavFile.version())); 1302 if (sameVersion && outNavFile.version() < 3.0 && navFile.glonass() != outNavFile.glonass()) { 1303 sameVersion = false; 1304 } 1305 QStringList hdr = navFile.headerRecords(); 1306 for (int ii = 0; ii < hdr.size(); ii++) { 1307 QString label = hdr[ii].mid(60).trimmed(); 1308 QString record = hdr[ii]; 1309 if (label == "LEAP SECONDS") { // RINEX 3 and 4: same format, RINEX 2: I6 1310 if (outNavFile.version() < 3.0) { 1311 record = record.left(6).leftJustified(60) + label; 1312 } 1313 } 1314 else if (!sameVersion) { 1315 QStringList conv = convertNavHeaderRecord(record, navFile.version(), 1316 outNavFile.version(), outNavFile.glonass()); 1317 if (conv.isEmpty()) { 1318 ++numNotConverted; 1319 if (!notConvertedLabels.contains(label)) { 1320 notConvertedLabels << label; 1321 } 1322 } 1323 for (int iConv = 0; iConv < conv.size(); iConv++) { 1324 QString key = navHeaderKey(conv[iConv]); 1325 if (!headerKeys.contains(key)) { 1326 headerKeys.insert(key); 1327 headerRecords << conv[iConv]; 1328 } 1329 } 1330 continue; 1331 } 1332 QString key = navHeaderKey(record); 1333 if (!headerKeys.contains(key)) { 1334 headerKeys.insert(key); 1335 headerRecords << record; 1336 } 1337 } 1338 1339 // STO/EOP/ION records (RINEX 4) 1340 const QList<t_rnxNavFile::t_rnxNavRecord>& recs = navFile.records(); 1341 for (int ii = 0; ii < recs.size(); ii++) { 1342 const t_rnxNavFile::t_rnxNavRecord& rec = recs[ii]; 1343 if (rec.tt.valid() && 1344 ((_begTime.valid() && rec.tt < _begTime) || 1345 (_endTime.valid() && rec.tt > _endTime))) { 1346 continue; 1347 } 1348 QStringList trimmed; 1349 for (int iLine = 0; iLine < rec.lines.size(); iLine++) { 1350 trimmed << rec.lines[iLine].trimmed(); 1351 } 1352 QString key = trimmed.join("|"); 1353 if (recordKeys.contains(key)) { 1354 continue; 1355 } 1356 recordKeys.insert(key); 1357 records.append(rec); 1358 } 1359 } 1360 1361 std::stable_sort(records.begin(), records.end(), 1362 [](const t_rnxNavFile::t_rnxNavRecord& r1, 1363 const t_rnxNavFile::t_rnxNavRecord& r2) { 1364 if (!r1.tt.valid() || !r2.tt.valid()) { 1365 return !r1.tt.valid() && r2.tt.valid(); 1366 } 1367 return r1.tt < r2.tt; 1368 }); 1369 1370 // RINEX 3/2 output: STO/ION records as header records (header records of 1371 // the input files of the output version have priority) 1372 QStringList notConvertedRecords; 1373 if (outNavFile.version() < 4.0) { 1374 QStringList conv; 1375 numRecordsLost = navRecordsToHeader(records, outNavFile.version(), outNavFile.glonass(), 1376 conv, notConvertedRecords); 1377 for (int iConv = 0; iConv < conv.size(); iConv++) { 1378 QString key = navHeaderKey(conv[iConv]); 1379 if (!headerKeys.contains(key)) { 1380 headerKeys.insert(key); 1381 headerRecords << conv[iConv]; 1382 } 1383 } 1384 records.clear(); 1385 } 1386 1387 if (_logStream) { 1388 if (numNotConverted) { 1389 *_logStream << "Nav header records not converted to RINEX version " 1390 << outNavFile.version() << ": " << numNotConverted 1391 << " (" << notConvertedLabels.join(", ") << ")" << Qt::endl; 1392 } 1393 if (numRecordsLost) { 1394 *_logStream << "STO/EOP/ION records not convertible to RINEX version " 1395 << outNavFile.version() << ": " << numRecordsLost 1396 << " (" << notConvertedRecords.join(", ") << ")" << Qt::endl; 1397 } 853 1398 } 854 1399 } -
trunk/BNC/src/rinex/reqcedit.h
r11192 r11200 28 28 #include <QtCore> 29 29 #include "rnxobsfile.h" 30 #include "rnxnavfile.h" 30 31 #include "rnxnavfile.h" 31 32 #include "ephemeris.h" … … 66 67 void editEphemerides(); 67 68 void editRnxObsHeader(t_rnxObsFile& obsFile); 69 static QStringList expandFileNames(const QStringList& fileNames); 70 static QStringList headerChangeRecords(const t_rnxObsHeader& header1, 71 const t_rnxObsHeader& header2, double outVersion); 72 static int navRecordsToHeader(const QList<t_rnxNavFile::t_rnxNavRecord>& records, 73 double outVersion, bool outGlonass, 74 QStringList& headerRecords, QStringList& notConverted); 75 static QStringList convertNavHeaderRecord(const QString& record, double inVersion, 76 double outVersion, bool outGlonass); 77 void collectNavRecords(const t_rnxNavFile& outNavFile, QStringList& headerRecords, 78 QList<t_rnxNavFile::t_rnxNavRecord>& records); 68 79 void collectEvents(const QList<t_rnxObsFile::t_rnxEvent>& events, 69 80 QList<t_rnxObsFile::t_rnxEvent>& pendingEvents) const; -
trunk/BNC/src/rinex/rnxnavfile.cpp
r11192 r11200 52 52 //////////////////////////////////////////////////////////////////////////// 53 53 t_rnxNavFile::t_rnxNavHeader::t_rnxNavHeader() { 54 _mergedFiles = 0; 54 55 _version = 0.0; 55 56 _glonass = false; … … 86 87 else if (key == "PGM / RUN BY / DATE") { 87 88 _runByDate.append(value.trimmed()); 89 } 90 else if (key == "MERGED FILE") { 91 // number of merged files, set for the written file 92 _mergedFiles = qMax(1, value.toInt()); 93 } 94 else if (!key.isEmpty()) { 95 // e.g. LEAP SECONDS, IONOSPHERIC CORR, TIME SYSTEM CORR, ION ALPHA, 96 // ION BETA, DELTA-UTC: A0,A1,T,W, DOI, LICENSE OF USE: kept verbatim 97 _otherRecords.append(line.left(60).leftJustified(60) + key); 88 98 } 89 99 } … … 153 163 QString prn; 154 164 bool ephPending = false; // RINEX 4: "> EPH" line read, data lines follow 165 bool recordPending = false; // RINEX 4: other record, data lines follow 155 166 156 167 while (stream->status() == QTextStream::Ok && !stream->atEnd()) { … … 177 188 navType = ""; 178 189 } 179 // RINEX version 4: data lines of an EPH record follow its "> EPH" line ;180 // anything else (e.g. the remainder of arecordthat could not be read)181 // is skipped 190 // RINEX version 4: data lines of an EPH record follow its "> EPH" line, 191 // those of other records (STO, EOP, ION, unknown types) are kept 192 // verbatim up to the next ">" line; anything else is skipped 182 193 else if (version() >= 4.0 && firstStr != ">") { 194 if (recordPending) { 195 t_rnxNavRecord& record = _records.last(); 196 if (record.lines.size() == 1) { // first data line: epoch 197 QTextStream in(line.mid(4, 19).toLatin1(), QIODevice::ReadOnly); 198 int year = 0, month = 0, day = 0, hour = 0, min = 0; 199 double sec = 0.0; 200 in >> year >> month >> day >> hour >> min >> sec; 201 if (year > 0) { 202 record.tt.set(year, month, day, hour, min, sec); 203 } 204 } 205 record.lines << line; 206 continue; 207 } 183 208 if (!ephPending || line.left(1) != prn.left(1)) { 184 209 ephPending = false; … … 189 214 } 190 215 else if (version() >= 4.0 && firstStr == ">") { 191 ephPending = false; 216 ephPending = false; 217 recordPending = false; 192 218 if (hlp.size() < 4) { 193 219 continue; … … 197 223 sys = prn[0].toLatin1(); 198 224 navType = hlp.at(3); 199 ephPending = (key == "EPH"); 200 201 // ALL Non-EPH messages are currently ignored 202 int lines2skip = 0; 203 // STO 204 if (key == "STO") { 205 lines2skip = 2; 206 } 207 // EOP 208 else if (key == "EOP") { 209 lines2skip = 3; 210 } 211 // ION 212 else if (key == "ION") { 213 if (sys == 'R') { 214 lines2skip = 1; //if (navType == "LXOC") {} 215 } 216 else if (sys == 'E') { 217 lines2skip = 2; 218 } 219 else if (sys == 'G' || sys == 'C' || 220 sys == 'J' || sys == 'I') { 221 lines2skip = 3; 222 if ((sys == 'I' && navType == "L1NV") || // I: KLOB, NEQN 223 (sys == 'J' && navType == "CNVX")) { // J: WIDE, JAPN 224 QString navSubType = hlp.value(4); // RINEX >= 4.02 only 225 if (navSubType == "KLOB") { 226 lines2skip += 1; 227 } 228 else if (navSubType == "NEQN") { 229 lines2skip += 4; 230 } 231 } 232 } 233 } 234 if (lines2skip) { 235 for (int ii = 0; ii < lines2skip; ii++) { 236 stream->readLine(); 237 } 225 if (key == "EPH") { 226 ephPending = true; 227 } 228 else { 229 t_rnxNavRecord record; 230 record.key = key; 231 record.lines << line; 232 _records.append(record); 233 recordPending = true; 238 234 } 239 235 continue; … … 379 375 // 380 376 //////////////////////////////////////////////////////////////////////////// 381 void t_rnxNavFile::writeHeader(const QMap<QString, QString>* txtMap, int numMergedFiles, int leapSecs) { 377 void t_rnxNavFile::writeHeader(const QMap<QString, QString>* txtMap, int numMergedFiles, int leapSecs, 378 const QStringList* headerRecords) { 379 380 // LEAP SECONDS of the input header (may hold future leap seconds) preferred 381 // --------------------------------------------------------------------------- 382 bool haveLeapSecs = false; 383 if (headerRecords) { 384 for (int ii = 0; ii < headerRecords->size(); ii++) { 385 if (headerRecords->at(ii).mid(60).trimmed() == "LEAP SECONDS") { 386 haveLeapSecs = true; 387 } 388 } 389 } 390 382 391 383 392 QString runBy = BNC_CORE->userName(); … … 459 468 << "PGM / RUN BY / DATE\n"; 460 469 } 461 *_stream << QString("%1").arg(leapSecs, 6, 10, QLatin1Char(' ')).left(60).leftJustified(60) 462 << "LEAP SECONDS\n"; 463 464 *_stream << QString("%1").arg(numMergedFiles, 19, 10, QLatin1Char(' ')).leftJustified(60) 465 << "MERGED FILE\n"; 470 if (!haveLeapSecs) { 471 *_stream << QString("%1").arg(leapSecs, 6, 10, QLatin1Char(' ')).left(60).leftJustified(60) 472 << "LEAP SECONDS\n"; 473 } 474 475 if (numMergedFiles > 1) { 476 *_stream << QString("%1").arg(numMergedFiles, 9).leftJustified(60) 477 << "MERGED FILE\n"; 478 } 466 479 } 467 480 … … 471 484 } 472 485 486 if (headerRecords) { 487 for (int ii = 0; ii < headerRecords->size(); ii++) { 488 *_stream << headerRecords->at(ii) << "\n"; 489 } 490 } 491 473 492 *_stream << QString() 474 493 .leftJustified(60) … … 476 495 } 477 496 497 // Write RINEX 4 STO, EOP or ION record (verbatim) 498 //////////////////////////////////////////////////////////////////////////// 499 void t_rnxNavFile::writeRecord(const t_rnxNavRecord& record) { 500 if (version() < 4.0) { 501 return; 502 } 503 for (int ii = 0; ii < record.lines.size(); ii++) { 504 QString line = record.lines[ii]; 505 int len = line.length(); 506 while (len > 0 && line[len-1] == ' ') { 507 len--; 508 } 509 line.truncate(len); 510 *_stream << line << "\n"; 511 } 512 } 513 478 514 // 479 515 //////////////////////////////////////////////////////////////////////////// -
trunk/BNC/src/rinex/rnxnavfile.h
r11023 r11200 55 55 QStringList _comments; 56 56 QStringList _runByDate; 57 QStringList _otherRecords; // other header records, verbatim 58 int _mergedFiles; // MERGED FILE (0: not given) 57 59 }; 58 60 59 61 public: 62 // RINEX 4 STO, EOP and ION records (and records of unknown type), verbatim 63 class t_rnxNavRecord { 64 public: 65 QString key; // STO, EOP, ION, ... 66 bncTime tt; // epoch of the first data line (for sorting) 67 QStringList lines; // incl. the "> " line 68 }; 69 60 70 t_rnxNavFile(const QString& fileName, e_inpOut inpOut); 61 71 ~t_rnxNavFile(); … … 68 78 QStringList comments() const {return _header._comments;} 69 79 QStringList runByDate() const {return _header._runByDate;} 80 QStringList headerRecords() const {return _header._otherRecords;} 81 int mergedFiles() const {return _header._mergedFiles;} 82 const QList<t_rnxNavRecord>& records() const {return _records;} 70 83 t_eph::e_system satSystem() const {return _header._satSys;} 71 84 void setGlonass(bool glo) {_header._glonass = glo;} 72 85 void setGnssTypeV3(t_eph::e_system system) {_header._satSys = system;} 73 void writeHeader(const QMap<QString, QString>* txtMap = 0, int numMergedFiles = 0, int leapSecs = 0); 86 void writeHeader(const QMap<QString, QString>* txtMap = 0, int numMergedFiles = 0, int leapSecs = 0, 87 const QStringList* headerRecords = 0); 74 88 void writeEph(const t_eph* eph); 89 void writeRecord(const t_rnxNavRecord& record); 75 90 76 91 protected: … … 88 103 QTextStream* _stream; 89 104 std::vector<t_eph*> _ephs; 105 QList<t_rnxNavRecord> _records; 90 106 t_rnxNavHeader _header; 91 107 }; -
trunk/BNC/src/rinex/rnxobsfile.cpp
r11192 r11200 701 701 } 702 702 703 // Station records of the header (marker, observer, receiver, antenna, 704 // position), also used for the header changes between concatenated files 705 //////////////////////////////////////////////////////////////////////////// 706 QStringList t_rnxObsHeader::stationRecords(double version) const { 707 if (version == 0.0) { 708 version = _version; 709 } 710 QStringList records; 711 // RINEX 3/4: MARKER NAME A60, RINEX 2: 4-character station ID 712 QString markerName = _markerName; 713 if (version < 3.0) { 714 markerName = _markerName.left(4); 715 } 716 records << QString("%1") 717 .arg(markerName, -60) 718 .leftJustified(60) 719 + "MARKER NAME"; 720 721 722 if (!_markerNumber.isEmpty()) { 723 records << QString("%1") 724 .arg(_markerNumber, -20) 725 .leftJustified(60) 726 + "MARKER NUMBER"; 727 } 728 729 if (version >= 3.0) { 730 records << QString("%1") 731 .arg(_markerType, -60) 732 .leftJustified(60) 733 + "MARKER TYPE"; 734 } 735 736 records << QString("%1%2") 737 .arg(_observer, -20) 738 .arg(_agency, -40) 739 .leftJustified(60) 740 + "OBSERVER / AGENCY"; 741 742 records << QString("%1%2%3") 743 .arg(_receiverNumber, -20) 744 .arg(_receiverType, -20) 745 .arg(_receiverVersion, -20) 746 .leftJustified(60) 747 + "REC # / TYPE / VERS"; 748 749 records << QString("%1%2") 750 .arg(_antennaNumber, -20) 751 .arg(_antennaName, -20) 752 .leftJustified(60) 753 + "ANT # / TYPE"; 754 755 records << QString("%1%2%3") 756 .arg(_xyz(1), 14, 'f', 4) 757 .arg(_xyz(2), 14, 'f', 4) 758 .arg(_xyz(3), 14, 'f', 4) 759 .leftJustified(60) 760 + "APPROX POSITION XYZ"; 761 762 records << QString("%1%2%3") 763 .arg(_antNEU(3), 14, 'f', 4) 764 .arg(_antNEU(2), 14, 'f', 4) 765 .arg(_antNEU(1), 14, 'f', 4) 766 .leftJustified(60) 767 + "ANTENNA: DELTA H/E/N"; 768 769 if (_rcvClkOffsAppl >= 0) { 770 records << QString("%1") 771 .arg(_rcvClkOffsAppl, 6) 772 .leftJustified(60) 773 + "RCV CLOCK OFFS APPL"; 774 } 775 return records; 776 } 777 703 778 // Write Header 704 779 //////////////////////////////////////////////////////////////////////////// … … 764 839 *stream << itCmnt.next().trimmed().left(60).leftJustified(60) << "COMMENT\n"; 765 840 } 766 QString markerName = _markerName.left(9); 767 if (_version < 3.0) { 768 markerName = _markerName.left(4); 769 } 770 *stream << QString("%1") 771 .arg(markerName, -60) 772 .leftJustified(60) 773 << "MARKER NAME\n"; 774 775 776 if (!_markerNumber.isEmpty()) { 777 *stream << QString("%1") 778 .arg(_markerNumber, -20) 779 .leftJustified(60) 780 << "MARKER NUMBER\n"; 781 } 782 783 if (_version >= 3.0) { 784 *stream << QString("%1") 785 .arg(_markerType, -60) 786 .leftJustified(60) 787 << "MARKER TYPE\n"; 788 } 789 790 *stream << QString("%1%2") 791 .arg(_observer, -20) 792 .arg(_agency, -40) 793 .leftJustified(60) 794 << "OBSERVER / AGENCY\n"; 795 796 *stream << QString("%1%2%3") 797 .arg(_receiverNumber, -20) 798 .arg(_receiverType, -20) 799 .arg(_receiverVersion, -20) 800 .leftJustified(60) 801 << "REC # / TYPE / VERS\n"; 802 803 *stream << QString("%1%2") 804 .arg(_antennaNumber, -20) 805 .arg(_antennaName, -20) 806 .leftJustified(60) 807 << "ANT # / TYPE\n"; 808 809 *stream << QString("%1%2%3") 810 .arg(_xyz(1), 14, 'f', 4) 811 .arg(_xyz(2), 14, 'f', 4) 812 .arg(_xyz(3), 14, 'f', 4) 813 .leftJustified(60) 814 << "APPROX POSITION XYZ\n"; 815 816 *stream << QString("%1%2%3") 817 .arg(_antNEU(3), 14, 'f', 4) 818 .arg(_antNEU(2), 14, 'f', 4) 819 .arg(_antNEU(1), 14, 'f', 4) 820 .leftJustified(60) 821 << "ANTENNA: DELTA H/E/N\n"; 822 823 if (_rcvClkOffsAppl >= 0) { 824 *stream << QString("%1") 825 .arg(_rcvClkOffsAppl, 6) 826 .leftJustified(60) 827 << "RCV CLOCK OFFS APPL\n"; 828 } 841 *stream << stationRecords().join("\n") << "\n"; 829 842 830 843 if (_version < 3.0) { -
trunk/BNC/src/rinex/rnxobsfile.h
r11192 r11200 76 76 int scaleFactor(char sys, const QString& type) const; 77 77 static QString convertRecord(const QString& line, double outVersion, double inVersion); 78 QStringList stationRecords(double version = 0.0) const; 79 QStringList otherRecords() const {return _otherRecords;} 80 double otherRecordsVersion() const {return _otherRecordsVersion;} 78 81 void setStartTime(const bncTime& startTime) {_startTime = startTime;} 79 82 -
trunk/BNC/tests/README.md
r11192 r11200 28 28 | `data/rnx402_MO.rnx`, `data/rnx402_MN.rnx` | crafted RINEX 4.02 files: 3 BRUX epochs with receiver clock offset, pico-sec digits and `RCV CLOCK OFFS APPL`; GLONASS FDMA/L1OC/L3OC ephemerides and STO/EOP/ION examples of the RINEX 4.02 document (ION with subtypes KLOB/NEQN, ION J CNVX without subtype) | 29 29 | `data/rnx402_events_MO.rnx` | `data/rnx402_MO.rnx` with event records (flags 1-6, blank epochs, pico-sec event epoch, events after the last epoch) and further header records (SYS / SCALE FACTOR, DCBS/PCVS APPLIED, CENTER OF MASS: XYZ, PRN / # OF OBS, an unknown record) | 30 | `data/nav402_p?_MN.rnx`, `data/nav305_?_MN.rnx`, `data/nav211_?.26n` | navigation files for the concatenation: 3 overlapping pieces of the 1 h BRUX RINEX 4.01 file (with STO/ION records); 2 RINEX 3.05 and 2 RINEX 2.11 GPS files (converted by BNC) with added IONOSPHERIC CORR/TIME SYSTEM CORR/LEAP SECONDS resp. ION ALPHA/ION BETA/DELTA-UTC records, differing between the files | 31 | `data/nav401_gps_MN.rnx` | GPS LNAV records of the daily BRUX RINEX 4.01 file incl. ION and STO records (conversion to RINEX 3/2 header records) | 32 | `data/obs_p1_MO.rnx`, `data/obs_p2_MO.rnx` | epochs 0-14 and 10-24 of the 1 h BRUX observation file, the second with another receiver firmware and antenna | 33 | `data/nav402_navic_MN.rnx` | NavIC STO IRUT records for UTC(NPLI) and UTCIRN (RINEX 4.02 examples) and one GPS ephemeris | 30 34 | `tools/fakecaster.py` | local Ntrip caster recording an upload as a BNC raw file (case key `fakecaster`); optionally TLS and serving a stream | 31 35 | `tools/bncraw.py` | `info` (streams, time span, RTCM3 message types), `slice` and `crs` (decode 1300/1301/1302) for raw captures | … … 62 66 types), scaled observations, header records kept, TIME OF LAST OBS, 63 67 # OF SATELLITES and PRN / # OF OBS of the written file. 68 * `rinex_nav_concat_v4`, `rinex_nav_concat_v4_to_v3`, `rinex_nav_concat_v3`, 69 `rinex_nav_concat_v3_to_v4`, `rinex_nav_concat_v2`: navigation file 70 concatenation: duplicates removed, RINEX 4 STO/ION records and header 71 records (first one of a kind) kept, records not convertible to the output 72 version reported in the log; `rinex_nav_concat_v4` equals the edit of the 73 original 1 h file. 74 * `rinex_nav_v4_gps_to_v2`, `rinex_nav_v4_gps_to_v3`, `rinex_nav_v2_to_v3`: 75 conversion of RINEX 4 ION/STO records to RINEX 2/3 header records (ION 76 ALPHA/BETA, DELTA-UTC resp. IONOSPHERIC CORR, TIME SYSTEM CORR) and of 77 RINEX 2 to RINEX 3 header records; `rinex_nav_concat_v4_to_v3` also covers 78 BDS (time mark, GEO/IGSO priority), Galileo, GLONASS. 79 * `rinex_nav_navic_irut_to_v3`: of two NavIC IRUT offsets, the one to UTCIRN 80 becomes TIME SYSTEM CORR IRUT in RINEX 3. 81 * `rinex_edit_long_marker`: marker name longer than 9 characters kept in RINEX 4. 82 * `rinex_concat_overlap`: overlapping observation files (epochs written once) 83 with header changes between the files (event flag 4). 64 84 * Stream replays of the real-time examples (`replay_*`), all from the 5 min 65 85 slice `data/wtzr_cnes_5min.raw` (BCEP00BKG0, WTZR00DEU0, SSRA00CNE1):
Note:
See TracChangeset
for help on using the changeset viewer.
