INSTINCT Code Coverage Report


Directory: src/
File: Nodes/DataProvider/GNSS/FileReader/RinexObsFile.cpp
Date: 2025-11-25 23:34:18
Exec Total Coverage
Lines: 301 391 77.0%
Functions: 17 21 81.0%
Branches: 496 1040 47.7%

Line Branch Exec Source
1 // This file is part of INSTINCT, the INS Toolkit for Integrated
2 // Navigation Concepts and Training by the Institute of Navigation of
3 // the University of Stuttgart, Germany.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9 #include "RinexObsFile.hpp"
10
11 #include <fmt/ranges.h>
12
13 #include "util/Eigen.hpp"
14
15 #include "util/Logger.hpp"
16 #include "internal/FlowManager.hpp"
17 #include "internal/gui/widgets/HelpMarker.hpp"
18
19 #include "util/StringUtil.hpp"
20
21 #include "Navigation/GNSS/Core/SatelliteSystem.hpp"
22 #include "Navigation/GNSS/Core/Code.hpp"
23 #include "NodeData/GNSS/GnssObs.hpp"
24
25 #include "util/Vendor/RINEX/RINEXUtilities.hpp"
26
27 namespace NAV
28 {
29
30 163 RinexObsFile::RinexObsFile()
31
4/8
✓ Branch 1 taken 163 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 163 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 163 times.
✗ Branch 9 not taken.
✓ Branch 13 taken 163 times.
✗ Branch 14 not taken.
163 : Node(typeStatic())
32 {
33 LOG_TRACE("{}: called", name);
34
35 163 _hasConfig = true;
36 163 _guiConfigDefaultWindowSize = { 517, 118 };
37
38
4/8
✓ Branch 1 taken 163 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 163 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 163 times.
✓ Branch 9 taken 163 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
489 CreateOutputPin("GnssObs", Pin::Type::Flow, { NAV::GnssObs::type() }, &RinexObsFile::pollData);
39 326 }
40
41 424 RinexObsFile::~RinexObsFile()
42 {
43 LOG_TRACE("{}: called", nameId());
44 424 }
45
46 277 std::string RinexObsFile::typeStatic()
47 {
48
1/2
✓ Branch 1 taken 277 times.
✗ Branch 2 not taken.
554 return "RinexObsFile";
49 }
50
51 std::string RinexObsFile::type() const
52 {
53 return typeStatic();
54 }
55
56 114 std::string RinexObsFile::category()
57 {
58
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
228 return "Data Provider";
59 }
60
61 void RinexObsFile::guiConfig()
62 {
63 if (auto res = FileReader::guiConfig(R"(Rinex Obs (.obs .rnx .*o){.obs,.rnx,(.+[.]\d\d?[oO])},.*)",
64 { ".obs", ".rnx", "(.+[.]\\d\\d?[oO])" }, size_t(id), nameId()))
65 {
66 LOG_DEBUG("{}: Path changed to {}", nameId(), _path);
67 flow::ApplyChanges();
68 if (res == FileReader::PATH_CHANGED)
69 {
70 doReinitialize();
71 }
72 else
73 {
74 doDeinitialize();
75 }
76 }
77 ImGui::Text("Supported versions: ");
78 std::ranges::for_each(_supportedVersions, [](double x) {
79 ImGui::SameLine();
80 ImGui::Text("%0.2f", x);
81 });
82
83 ImGui::Checkbox("Erase less precise codes", &_eraseLessPreciseCodes);
84 ImGui::SameLine();
85 gui::widgets::HelpMarker("Whether to remove less precise codes (e.g. if G1X (L1C combined) is present, don't use G1L (L1C pilot) and G1S (L1C data))");
86 }
87
88 [[nodiscard]] json RinexObsFile::save() const
89 {
90 LOG_TRACE("{}: called", nameId());
91
92 json j;
93
94 j["FileReader"] = FileReader::save();
95 j["eraseLessPreciseCodes"] = _eraseLessPreciseCodes;
96
97 return j;
98 }
99
100 49 void RinexObsFile::restore(json const& j)
101 {
102 LOG_TRACE("{}: called", nameId());
103
104
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 if (j.contains("FileReader"))
105 {
106 49 FileReader::restore(j.at("FileReader"));
107 }
108
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 if (j.contains("eraseLessPreciseCodes"))
109 {
110 49 j.at("eraseLessPreciseCodes").get_to(_eraseLessPreciseCodes);
111 }
112 49 }
113
114 49 bool RinexObsFile::initialize()
115 {
116 LOG_TRACE("{}: called", nameId());
117
118 49 return FileReader::initialize();
119 }
120
121 49 void RinexObsFile::deinitialize()
122 {
123 LOG_TRACE("{}: called", nameId());
124
125 49 FileReader::deinitialize();
126
127 49 _version = 0.0;
128 49 _timeSystem = TimeSys_None;
129 49 _obsDescription.clear();
130 49 _rcvClockOffsAppl = false;
131 49 _receiverInfo = {};
132 98 }
133
134 98 bool RinexObsFile::resetNode()
135 {
136 LOG_TRACE("{}: called", nameId());
137
138 98 FileReader::resetReader();
139
140 98 return true;
141 }
142
143 49 FileReader::FileType RinexObsFile::determineFileType()
144 {
145 1625 auto extHeaderLabel = [](std::string line) {
146 // Remove any trailing non text characters
147
2/4
✓ Branch 3 taken 1635 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1635 times.
✗ Branch 8 not taken.
120742 line.erase(std::ranges::find_if(line, [](int ch) { return std::iscntrl(ch); }), line.end());
148
149
1/2
✓ Branch 2 taken 1635 times.
✗ Branch 3 not taken.
1635 return str::trim_copy(std::string_view(line).substr(60, 20));
150 };
151
152
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 std::filesystem::path filepath = getFilepath();
153
154
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
49 auto filestreamHeader = std::ifstream(filepath);
155
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
48 if (filestreamHeader.good())
156 {
157 49 std::string line;
158 // --------------------------------------- RINEX VERSION / TYPE ------------------------------------------
159
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 std::getline(filestreamHeader, line);
160
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 str::rtrim(line);
161
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
49 if (line.size() != 80)
162 {
163 LOG_ERROR("{}: Not a valid RINEX OBS file. Lines should be 80 characters long but the file has {}.", nameId(), line.size() - 1);
164 return FileReader::FileType::NONE;
165 }
166
167
3/6
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 49 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 49 times.
49 if (extHeaderLabel(line) != "RINEX VERSION / TYPE")
168 {
169 LOG_ERROR("{}: Not a valid RINEX OBS file. Could not read 'RINEX VERSION / TYPE' line.", nameId());
170 return FileReader::FileType::NONE;
171 }
172
173
3/6
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✗ Branch 8 not taken.
49 double version = std::stod(str::trim_copy(line.substr(0, 20))); // FORMAT: F9.2,11X
174
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 49 times.
47 if (!_supportedVersions.contains(version))
175 {
176 LOG_ERROR("{}: RINEX version {} is not supported. Supported versions are [{}]", nameId(),
177 version, fmt::join(_supportedVersions.begin(), _supportedVersions.end(), ", "));
178 return FileReader::FileType::NONE;
179 }
180
181
2/4
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 5 not taken.
49 std::string fileType = str::trim_copy(line.substr(20, 20)); // FORMAT: A1,19X
182
2/4
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 46 times.
47 if (fileType.at(0) != 'O')
183 {
184 LOG_ERROR("{}: Not a valid RINEX OBS file. File type '{}' not recognized.", nameId(), fileType);
185 doDeinitialize();
186 return FileReader::FileType::NONE;
187 }
188
2/4
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
46 std::string satSystem = str::trim_copy(line.substr(40, 20)); // FORMAT: A1,19X
189
7/12
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 46 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 43 times.
✓ Branch 8 taken 3 times.
✓ Branch 10 taken 45 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 45 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 48 times.
47 if (SatelliteSystem::fromChar(satSystem.at(0)) == SatSys_None && satSystem.at(0) != 'M')
190 {
191 LOG_ERROR("{}: Not a valid RINEX OBS file. Satellite System '{}' not recognized.", nameId(), satSystem.at(0));
192 doDeinitialize();
193 return FileReader::FileType::NONE;
194 }
195 // ---------------------------------------- PGM / RUN BY / DATE ------------------------------------------
196
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
48 std::getline(filestreamHeader, line);
197
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
47 str::rtrim(line);
198
3/6
✓ Branch 2 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 49 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 49 times.
48 if (extHeaderLabel(line) != "PGM / RUN BY / DATE")
199 {
200 LOG_ERROR("{}: Not a valid RINEX OBS file. Could not read 'PGM / RUN BY / DATE' line.", nameId());
201 return FileReader::FileType::NONE;
202 }
203
204 // ----------------------------------------- END OF HEADER -------------------------------------------
205
3/6
✓ Branch 1 taken 1532 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1532 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1532 times.
✗ Branch 7 not taken.
1538 while (std::getline(filestreamHeader, line))
206 {
207
1/2
✓ Branch 1 taken 1532 times.
✗ Branch 2 not taken.
1532 str::rtrim(line);
208
4/6
✓ Branch 2 taken 1530 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1535 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 49 times.
✓ Branch 10 taken 1489 times.
1532 if (extHeaderLabel(line) == "END OF HEADER")
209 {
210 49 return FileReader::FileType::ASCII;
211 }
212 }
213 LOG_ERROR("{}: Not a valid RINEX NAV file. Could not read 'END OF HEADER' line.", nameId());
214 return FileReader::FileType::NONE;
215 49 }
216
217 LOG_ERROR("{}: Could not determine file type because file could not be opened '{}' line.", nameId(), filepath.string());
218 return FileReader::FileType::NONE;
219 49 }
220
221 49 void RinexObsFile::readHeader()
222 {
223 LOG_TRACE("{}: called", nameId());
224
225 49 std::string line;
226
227 // --------------------------------------- RINEX VERSION / TYPE ------------------------------------------
228
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
48 getline(line);
229
3/6
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 47 times.
✗ Branch 8 not taken.
49 _version = std::stod(str::trim_copy(line.substr(0, 20)));
230
3/6
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 49 times.
✗ Branch 9 not taken.
48 LOG_DEBUG("{}: Version: {:3.2f}", nameId(), _version); // FORMAT: F9.2,11X
231
5/10
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 49 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 49 times.
✗ Branch 15 not taken.
49 LOG_DEBUG("{}: SatSys : {}", nameId(), str::trim_copy(line.substr(40, 20))); // FORMAT: A1,19X
232
233 // #######################################################################################################
234
6/12
✓ Branch 1 taken 1485 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1485 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1485 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1486 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1486 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1486 times.
✗ Branch 14 not taken.
1485 while (getline(line) && !eof())
235 {
236
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1486 times.
1486 if (line.size() < 60)
237 {
238 LOG_WARN("{}: Skipping header line because it does not include a header label: '{}'", nameId(), line);
239 continue;
240 }
241
2/4
✓ Branch 1 taken 1485 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1486 times.
✗ Branch 5 not taken.
1486 auto headerLabel = str::trim_copy(line.substr(60, 20));
242
3/4
✓ Branch 1 taken 1485 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1436 times.
✓ Branch 4 taken 49 times.
1486 if (headerLabel == "PGM / RUN BY / DATE")
243 {
244 // Name of program creating current file
245 LOG_DATA("{}: Program: {}", nameId(), str::trim_copy(line.substr(0, 20))); // FORMAT: A20
246 // Name of agency creating current file
247 LOG_DATA("{}: Run by : {}", nameId(), str::trim_copy(line.substr(20, 20))); // FORMAT: A20
248 // Date and time of file creation
249 LOG_DATA("{}: Date : {}", nameId(), str::trim_copy(line.substr(40, 20))); // FORMAT: A20
250 }
251
3/4
✓ Branch 1 taken 1435 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1342 times.
✓ Branch 4 taken 93 times.
1436 else if (headerLabel == "COMMENT")
252 {
253 LOG_DATA("{}: Comment: {}", nameId(), line.substr(0, 60)); // FORMAT: A60
254 }
255
3/4
✓ Branch 1 taken 1341 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 1292 times.
1342 else if (headerLabel == "MARKER NAME")
256 {
257
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 auto markerName = str::trim_copy(line.substr(0, 60)); // FORMAT: A60
258 49 if (!markerName.empty())
259 {
260 LOG_DATA("{}: Marker name: {}", nameId(), markerName);
261 }
262 49 }
263
3/4
✓ Branch 1 taken 1293 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 37 times.
✓ Branch 4 taken 1256 times.
1292 else if (headerLabel == "MARKER NUMBER")
264 {
265
2/4
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 37 times.
✗ Branch 5 not taken.
37 auto markerNumber = str::trim_copy(line.substr(0, 20)); // FORMAT: A20
266 37 if (!markerNumber.empty())
267 {
268 LOG_DATA("{}: Marker number: {}", nameId(), markerNumber);
269 }
270 37 }
271
3/4
✓ Branch 1 taken 1258 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 1214 times.
1256 else if (headerLabel == "MARKER TYPE")
272 {
273
2/4
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
44 auto markerType = str::trim_copy(line.substr(0, 60)); // FORMAT: A20,40X
274 44 if (!markerType.empty())
275 {
276 LOG_DATA("{}: Marker type: {}", nameId(), markerType);
277 }
278 44 }
279
3/4
✓ Branch 1 taken 1212 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 1163 times.
1214 else if (headerLabel == "OBSERVER / AGENCY")
280 {
281
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 auto observer = str::trim_copy(line.substr(0, 20)); // FORMAT: A20,A40
282
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 auto agency = str::trim_copy(line.substr(20, 40));
283
3/4
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 36 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 13 times.
49 if (!observer.empty() || !agency.empty())
284 {
285 LOG_DATA("{}: Observer '{}', Agency '{}'", nameId(), observer, agency);
286 }
287 49 }
288
3/4
✓ Branch 1 taken 1162 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 1113 times.
1163 else if (headerLabel == "REC # / TYPE / VERS")
289 {
290
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 auto receiverNumber = str::trim_copy(line.substr(0, 20)); // FORMAT: 3A20
291
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 auto receiverType = str::trim_copy(line.substr(20, 20));
292
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 auto receiverVersion = str::trim_copy(line.substr(40, 20));
293
5/6
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 35 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 10 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
49 if (!receiverNumber.empty() || !receiverType.empty() || !receiverVersion.empty())
294 {
295 LOG_DATA("{}: RecNum '{}', recType '{}', recVersion '{}'", nameId(),
296 receiverNumber, receiverType, receiverVersion);
297 }
298 49 }
299
3/4
✓ Branch 1 taken 1112 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 1063 times.
1113 else if (headerLabel == "ANT # / TYPE")
300 {
301
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 auto antennaNumber = str::trim_copy(line.substr(0, 20)); // FORMAT: 2A20
302
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 auto antennaType = str::trim_copy(line.substr(20, 20));
303
4/4
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 32 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 14 times.
49 if (!antennaNumber.empty() || !antennaType.empty())
304 {
305 LOG_DATA("{}: antNum '{}', antType '{}'", nameId(), antennaNumber, antennaType);
306 }
307
5/8
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 35 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 49 times.
✗ Branch 9 not taken.
49 if (!antennaType.empty() || antennaType != "Unknown")
308 {
309
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 _receiverInfo.antennaType = antennaType;
310 }
311 49 }
312
3/4
✓ Branch 1 taken 1064 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 1020 times.
1063 else if (headerLabel == "APPROX POSITION XYZ")
313 {
314 // Geocentric approximate marker position (Units: Meters, System: ITRS recommended)
315 // Optional for moving platforms
316
3/6
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 44 times.
✗ Branch 8 not taken.
88 Eigen::Vector3d position_xyz{ std::stod(str::trim_copy(line.substr(0, 14))),
317
3/6
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 44 times.
✗ Branch 8 not taken.
88 std::stod(str::trim_copy(line.substr(14, 14))),
318
4/8
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 44 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 44 times.
✗ Branch 11 not taken.
44 std::stod(str::trim_copy(line.substr(28, 14))) }; // FORMAT: 3F14.4
319
320 LOG_DATA("{}: Approx Position XYZ: {} (not used yet)", nameId(), position_xyz.transpose());
321
322
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
44 _receiverInfo.e_approxPos = position_xyz;
323 }
324
3/4
✓ Branch 1 taken 1018 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 970 times.
1020 else if (headerLabel == "ANTENNA: DELTA H/E/N")
325 {
326 // Antenna height: Height of the antenna reference point (ARP) above the marker [m]
327
3/6
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 48 times.
✗ Branch 8 not taken.
48 double antennaHeight = std::stod(str::trim_copy(line.substr(0, 14))); // FORMAT: F14.4,
328 // Horizontal eccentricity of ARP relative to the marker (east) [m]
329
3/6
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 48 times.
✗ Branch 8 not taken.
48 double antennaEccentricityEast = std::stod(str::trim_copy(line.substr(14, 14))); // FORMAT: 2F14.4,
330 // Horizontal eccentricity of ARP relative to the marker (north) [m]
331
3/6
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 48 times.
✗ Branch 8 not taken.
48 double antennaEccentricityNorth = std::stod(str::trim_copy(line.substr(28, 14)));
332
333 LOG_DATA("{}: Antenna delta H/E/N: {}, {}, {} (not used yet)", nameId(),
334 antennaHeight, antennaEccentricityEast, antennaEccentricityNorth);
335
336
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 _receiverInfo.antennaDeltaNEU = Eigen::Vector3d(antennaEccentricityNorth, antennaEccentricityEast, antennaHeight);
337 }
338
3/4
✓ Branch 1 taken 970 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 969 times.
970 else if (headerLabel == "ANTENNA: DELTA X/Y/Z")
339 {
340 // Position of antenna reference point for antenna on vehicle (m): XYZ vector in body-fixed coordinate system
341
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 [[maybe_unused]] Eigen::Vector3d antennaDeltaXYZ{ std::stod(str::trim_copy(line.substr(0, 14))),
342
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 std::stod(str::trim_copy(line.substr(14, 14))),
343
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
1 std::stod(str::trim_copy(line.substr(28, 14))) }; // FORMAT: 3F14.4
344
345 LOG_DATA("{}: Antenna Delta XYZ: {} (not used yet)", nameId(), antennaDeltaXYZ.transpose());
346 }
347
2/4
✓ Branch 1 taken 969 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 970 times.
✗ Branch 4 not taken.
969 else if (headerLabel == "ANTENNA: PHASECENTER")
348 {
349 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "ANTENNA: PHASECENTER");
350 }
351
2/4
✓ Branch 1 taken 972 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 972 times.
✗ Branch 4 not taken.
970 else if (headerLabel == "ANTENNA: B.SIGHT XYZ")
352 {
353 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "ANTENNA: B.SIGHT XYZ");
354 }
355
2/4
✓ Branch 1 taken 972 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 972 times.
✗ Branch 4 not taken.
972 else if (headerLabel == "ANTENNA: ZERODIR AZI")
356 {
357 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "ANTENNA: ZERODIR AZI");
358 }
359
2/4
✓ Branch 1 taken 972 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 972 times.
✗ Branch 4 not taken.
972 else if (headerLabel == "ANTENNA: ZERODIR XYZ")
360 {
361 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "ANTENNA: ZERODIR XYZ");
362 }
363
2/4
✓ Branch 1 taken 974 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 974 times.
✗ Branch 4 not taken.
972 else if (headerLabel == "CENTER OF MASS: XYZ")
364 {
365 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "CENTER OF MASS: XYZ");
366 }
367
3/4
✓ Branch 1 taken 972 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 226 times.
✓ Branch 4 taken 746 times.
974 else if (headerLabel == "SYS / # / OBS TYPES")
368 {
369 // Satellite system code (G/R/E/J/C/I/S) - FORMAT: A1,
370
2/4
✓ Branch 1 taken 226 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 226 times.
✗ Branch 5 not taken.
226 auto satSys = SatelliteSystem::fromChar(line.at(0));
371
372 // Number of different observation types for the specified satellite system - Format: 2X,I3,
373
2/4
✓ Branch 1 taken 226 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 224 times.
✗ Branch 5 not taken.
226 size_t numSpecifications = std::stoul(line.substr(3, 3));
374
375 226 std::string debugOutput;
376
2/2
✓ Branch 0 taken 2853 times.
✓ Branch 1 taken 226 times.
3079 for (size_t n = 0, nLine = 1, i = 7; n < numSpecifications; n++, nLine++, i += 4)
377 {
378 // Observation descriptors: Type, Band, Attribute - FORMAT 13(1X,A3)
379
380
2/4
✓ Branch 1 taken 2852 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2852 times.
✗ Branch 5 not taken.
2853 NAV::vendor::RINEX::ObsType type = NAV::vendor::RINEX::obsTypeFromChar(line.at(i));
381
2/4
✓ Branch 1 taken 2848 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2852 times.
✗ Branch 5 not taken.
2852 Frequency freq = NAV::vendor::RINEX::getFrequencyFromBand(satSys, line.at(i + 1) - '0');
382
1/2
✓ Branch 1 taken 2852 times.
✗ Branch 2 not taken.
2852 auto attribute = line.at(i + 2);
383
4/6
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 2804 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2852 times.
2852 if (freq == B01 && attribute == 'I') { freq = B02; }
384
1/2
✓ Branch 1 taken 2851 times.
✗ Branch 2 not taken.
2852 Code code = Code::fromFreqAttr(freq, attribute);
385
386
2/4
✓ Branch 1 taken 2850 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2852 times.
✗ Branch 5 not taken.
2851 _obsDescription[satSys].push_back(NAV::vendor::RINEX::ObservationDescription{ .type = type, .code = code });
387
388
2/4
✓ Branch 1 taken 2854 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2854 times.
✗ Branch 5 not taken.
5707 debugOutput += fmt::format("({},{},{})", NAV::vendor::RINEX::obsTypeToChar(type), freq, code);
389
390
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 2753 times.
2853 if (nLine == 13)
391 {
392
1/2
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
100 getline(line);
393 100 nLine = 0;
394 100 i = 3;
395 }
396 }
397
398 LOG_DATA("{}: Obs Type {} with {} specifications [{}]", nameId(),
399 satSys, numSpecifications, debugOutput);
400 226 }
401
3/4
✓ Branch 1 taken 745 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 718 times.
✓ Branch 4 taken 27 times.
746 else if (headerLabel == "SIGNAL STRENGTH UNIT")
402 {
403 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "SIGNAL STRENGTH UNIT");
404 }
405
3/4
✓ Branch 1 taken 718 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 683 times.
✓ Branch 4 taken 35 times.
718 else if (headerLabel == "INTERVAL")
406 {
407 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "INTERVAL");
408 }
409
3/4
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 635 times.
683 else if (headerLabel == "TIME OF FIRST OBS")
410 {
411
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 [[maybe_unused]] auto year = std::stoi(line.substr(0, 6));
412
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 [[maybe_unused]] auto month = std::stoi(line.substr(6, 6));
413
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 [[maybe_unused]] auto day = std::stoi(line.substr(12, 6));
414
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 [[maybe_unused]] auto hour = std::stoi(line.substr(18, 6));
415
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 [[maybe_unused]] auto min = std::stoi(line.substr(24, 6));
416
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
49 [[maybe_unused]] auto sec = std::stold(line.substr(30, 13));
417
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
48 _timeSystem = TimeSystem::fromString(line.substr(30 + 13 + 5, 3));
418 LOG_DATA("{}: Time of first obs: {} GPST (originally in '{}' time)", nameId(),
419 InsTime{ static_cast<uint16_t>(year),
420 static_cast<uint16_t>(month),
421 static_cast<uint16_t>(day),
422 static_cast<uint16_t>(hour),
423 static_cast<uint16_t>(min),
424 sec,
425 _timeSystem }
426 .toYMDHMS(GPST),
427 std::string(_timeSystem));
428 }
429
3/4
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 597 times.
✓ Branch 4 taken 38 times.
635 else if (headerLabel == "TIME OF LAST OBS")
430 {
431 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "TIME OF LAST OBS");
432 }
433
2/4
✓ Branch 1 taken 597 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 597 times.
597 else if (headerLabel == "RCV CLOCK OFFS APPL")
434 {
435 if (str::stoi(line.substr(0, 6), 0))
436 {
437 _rcvClockOffsAppl = true;
438 LOG_INFO("{}: Data (epoch, pseudorange, phase) corrected by the reported clock offset.", nameId());
439 }
440 LOG_TRACE("{}: Receiver clock offset applies: {}", nameId(), _rcvClockOffsAppl);
441 }
442
2/4
✓ Branch 1 taken 597 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 597 times.
✗ Branch 4 not taken.
597 else if (headerLabel == "SYS / DCBS APPLIED")
443 {
444 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "SYS / DCBS APPLIED");
445 }
446
2/4
✓ Branch 1 taken 598 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 598 times.
✗ Branch 4 not taken.
597 else if (headerLabel == "SYS / PCVS APPLIED")
447 {
448 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "SYS / PCVS APPLIED");
449 }
450
2/4
✓ Branch 1 taken 597 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 597 times.
✗ Branch 4 not taken.
598 else if (headerLabel == "SYS / SCALE FACTOR")
451 {
452 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "SYS / SCALE FACTOR");
453 }
454
3/4
✓ Branch 1 taken 597 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 181 times.
✓ Branch 4 taken 416 times.
597 else if (headerLabel == "SYS / PHASE SHIFT")
455 {
456 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "SYS / PHASE SHIFT");
457 }
458
3/4
✓ Branch 1 taken 181 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 129 times.
✓ Branch 4 taken 52 times.
181 else if (headerLabel == "GLONASS SLOT / FRQ #")
459 {
460 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "GLONASS SLOT / FRQ #");
461 }
462
3/4
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 89 times.
✓ Branch 4 taken 40 times.
129 else if (headerLabel == "GLONASS COD/PHS/BIS")
463 {
464 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "GLONASS COD/PHS/BIS");
465 }
466
3/4
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
✓ Branch 4 taken 20 times.
89 else if (headerLabel == "LEAP SECONDS")
467 {
468 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "LEAP SECONDS");
469 }
470
3/4
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 20 times.
69 else if (headerLabel == "# OF SATELLITES")
471 {
472 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "# OF SATELLITES");
473 }
474
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✗ Branch 4 not taken.
49 else if (headerLabel == "PRN / # OF OBS")
475 {
476 LOG_TRACE("{}: '{}' not implemented yet", nameId(), "PRN / # OF OBS");
477 }
478
2/4
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✗ Branch 4 not taken.
49 else if (headerLabel == "END OF HEADER")
479 {
480 49 break;
481 }
482 else
483 {
484 LOG_WARN("{}: Unknown header label '{}' in line '{}'", nameId(), headerLabel, line);
485 }
486 1482 }
487
488
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
49 if (_timeSystem == TimeSys_None) // If time system not set, try to apply default value
489 {
490 if (_obsDescription.size() == 1)
491 {
492 switch (SatelliteSystem_(_obsDescription.begin()->first))
493 {
494 case GPS:
495 _timeSystem = GPST;
496 break;
497 case GLO:
498 _timeSystem = GLNT;
499 break;
500 case GAL:
501 _timeSystem = UTC;
502 break;
503 case QZSS:
504 _timeSystem = QZSST;
505 break;
506 case BDS:
507 _timeSystem = BDT;
508 break;
509 case IRNSS:
510 _timeSystem = IRNSST;
511 break;
512 default:
513 LOG_CRITICAL("{}: Could not determine time system of the file because satellite system '{}' has no default.",
514 nameId(), SatelliteSystem(_obsDescription.begin()->first));
515 break;
516 }
517 }
518 else
519 {
520 LOG_CRITICAL("{}: Could not determine time system of the file.", nameId());
521 }
522 }
523 49 }
524
525 8633 std::shared_ptr<const NodeData> RinexObsFile::pollData()
526 {
527 8633 std::string line;
528
529 8631 InsTime epochTime;
530
531 // 0: OK | 1: power failure between previous and current epoch | > 1 : Special event
532 8631 int epochFlag = -1;
533 8631 size_t nSatellites = 0;
534
10/14
✓ Branch 0 taken 8630 times.
✓ Branch 1 taken 8583 times.
✓ Branch 3 taken 8631 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8582 times.
✓ Branch 6 taken 49 times.
✓ Branch 8 taken 8583 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 8582 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8583 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 8583 times.
✓ Branch 16 taken 8631 times.
17213 while (epochFlag != 0 && !eof() && getline(line)) // Read lines till epoch record with valid epoch flag
535 {
536
1/2
✓ Branch 1 taken 8584 times.
✗ Branch 2 not taken.
8583 str::trim(line);
537
538
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8584 times.
8584 if (line.empty())
539 {
540 continue;
541 }
542
2/4
✓ Branch 1 taken 8584 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8584 times.
✗ Branch 4 not taken.
8584 if (line.at(0) == '>') // EPOCH record - Record identifier: > - Format: A1,
543 {
544
2/4
✓ Branch 1 taken 8584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8584 times.
✗ Branch 5 not taken.
8584 auto year = std::stoi(line.substr(2, 4)); // Format: 1X,I4,
545
2/4
✓ Branch 1 taken 8584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8584 times.
✗ Branch 5 not taken.
8584 auto month = std::stoi(line.substr(7, 2)); // Format: 1X,I2.2,
546
2/4
✓ Branch 1 taken 8584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8584 times.
✗ Branch 5 not taken.
8584 auto day = std::stoi(line.substr(10, 2)); // Format: 1X,I2.2,
547
2/4
✓ Branch 1 taken 8584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8584 times.
✗ Branch 5 not taken.
8584 auto hour = std::stoi(line.substr(13, 2)); // Format: 1X,I2.2,
548
2/4
✓ Branch 1 taken 8584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8584 times.
✗ Branch 5 not taken.
8584 auto min = std::stoi(line.substr(16, 2)); // Format: 1X,I2.2,
549
2/4
✓ Branch 1 taken 8584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8584 times.
✗ Branch 5 not taken.
8584 auto sec = std::stold(line.substr(18, 11)); // Format: F11.7,2X,I1,
550
2/4
✓ Branch 1 taken 8583 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8582 times.
✗ Branch 5 not taken.
8583 nSatellites = std::stoul(line.substr(29 + 3, 3)); // Format: I3,6X,F15.12
551
552 8582 [[maybe_unused]] double recClkOffset = 0.0;
553 try
554 {
555
6/10
✓ Branch 1 taken 1450 times.
✓ Branch 2 taken 7131 times.
✓ Branch 4 taken 1450 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1450 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1450 times.
✓ Branch 10 taken 7131 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
8582 recClkOffset = line.size() >= 41 + 3 ? std::stod(line.substr(41, 15)) : 0.0; // Format: F15.12
556 }
557 catch (const std::exception& /* exception */)
558 {
559 LOG_DATA("{}: 'recClkOffset' not mentioned in file --> recClkOffset = {}", nameId(), recClkOffset);
560 }
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8579 times.
8579 if (_rcvClockOffsAppl)
562 {
563 sec -= recClkOffset;
564 }
565
566
1/2
✓ Branch 1 taken 8581 times.
✗ Branch 2 not taken.
8579 epochTime = InsTime{ static_cast<uint16_t>(year), static_cast<uint16_t>(month), static_cast<uint16_t>(day),
567 static_cast<uint16_t>(hour), static_cast<uint16_t>(min), sec,
568 _timeSystem };
569
570
2/4
✓ Branch 1 taken 8583 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8582 times.
✗ Branch 5 not taken.
8581 epochFlag = std::stoi(line.substr(31, 1)); // Format: 2X,I1,
571
572 LOG_DATA("{}: {}, epochFlag {}, numSats {}, recClkOffset {}", nameId(),
573 epochTime.toYMDHMS(), epochFlag, nSatellites, recClkOffset);
574 }
575 }
576
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 8582 times.
8631 if (epochTime.empty())
577 {
578 49 return nullptr;
579 }
580
581
1/2
✓ Branch 1 taken 8580 times.
✗ Branch 2 not taken.
8582 auto gnssObs = std::make_shared<GnssObs>();
582 8580 gnssObs->insTime = epochTime;
583
584 // TODO: while loop till eof() or epochFlag == 0 (in case some other flags in the file)
585
586 8578 size_t satCnt = 0;
587
12/16
✓ Branch 1 taken 275479 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 275475 times.
✓ Branch 4 taken 4 times.
✓ Branch 6 taken 275484 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 266950 times.
✓ Branch 9 taken 8534 times.
✓ Branch 11 taken 266938 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 266937 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 266893 times.
✓ Branch 17 taken 44 times.
✓ Branch 18 taken 266894 times.
✓ Branch 19 taken 8581 times.
275478 while (!eof() && peek() != '>' && getline(line)) // Read observation records till line with '>'
588 {
589
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 266893 times.
266894 if (line.empty())
590 {
591 continue;
592 }
593
2/4
✓ Branch 1 taken 266896 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 266895 times.
✗ Branch 5 not taken.
266893 auto satSys = SatelliteSystem::fromChar(line.at(0)); // Format: A1,
594
2/4
✓ Branch 1 taken 266888 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 266897 times.
✗ Branch 5 not taken.
266895 auto satNum = static_cast<uint8_t>(std::stoi(line.substr(1, 2))); // Format: I2.2,
595
596 LOG_DATA("{}: [{}] {}{}:", nameId(), gnssObs->insTime.toYMDHMS(GPST), char(satSys), satNum);
597
598 266882 size_t curExtractLoc = 3;
599
3/4
✓ Branch 1 taken 266878 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 2699108 times.
✓ Branch 9 taken 251376 times.
2950522 for (const auto& obsDesc : _obsDescription.at(satSys))
600 {
601
2/2
✓ Branch 1 taken 15396 times.
✓ Branch 2 taken 2683636 times.
2699053 if (line.size() < curExtractLoc + 14) // Remaining elements are all blank
602 {
603 15396 break;
604 }
605
606
2/4
✓ Branch 1 taken 2683086 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2682357 times.
✗ Branch 5 not taken.
2683636 auto strObs = str::trim_copy(line.substr(curExtractLoc, 14)); // Format: F14.3
607 2682300 curExtractLoc += 14;
608
2/2
✓ Branch 1 taken 368433 times.
✓ Branch 2 taken 2314326 times.
2682300 if (strObs.empty())
609 {
610 368433 curExtractLoc += 2;
611 368433 continue;
612 }
613 // Observation value depending on definition type
614 2314326 double observation{};
615 try
616 {
617
1/2
✓ Branch 1 taken 2315211 times.
✗ Branch 2 not taken.
2314326 observation = std::stod(strObs);
618 }
619 catch (const std::exception& e)
620 {
621 if ((*gnssObs)({ obsDesc.code, satNum }).pseudorange)
622 {
623 if (obsDesc.type == NAV::vendor::RINEX::ObsType::L) // Phase
624 {
625 LOG_WARN("{}: observation of satSys = {} contains no carrier phase. This happens if the CN0 is so small that the PLL could not lock, even if the DLL has locked (= pseudorange available). The observation is still valid.", nameId(), char(satSys));
626 }
627 else if (obsDesc.type == NAV::vendor::RINEX::ObsType::D) // Doppler
628 {
629 LOG_WARN("{}: observation of satSys = {} contains no doppler.", nameId(), char(satSys));
630 }
631 }
632 continue;
633 }
634
635 // TODO: Springer Handbook of Global Navigation, p. 1211 prefer attributes over others and let user decide also which ones to take into the calculation
636
637 // Loss of lock indicator
638 // Bit 0 set: Lost lock between previous and current observation: Cycle slip possible.
639 // For phase observations only. Note: Bit 0 is the least significant bit.
640 // Bit 1 set: Half-cycle ambiguity/slip possible. Software not capable of handling half
641 // cycles should skip this observation. Valid for the current epoch only.
642 // Bit 2 set: Galileo BOC-tracking of an MBOC-modulated signal (may suffer from increased noise).
643 2315211 uint8_t LLI = 0;
644
2/2
✓ Branch 1 taken 2267547 times.
✓ Branch 2 taken 47550 times.
2315211 if (line.size() > curExtractLoc)
645 {
646
1/2
✓ Branch 1 taken 2267437 times.
✗ Branch 2 not taken.
2267547 char LLIc = line.at(curExtractLoc);
647
2/2
✓ Branch 0 taken 2200191 times.
✓ Branch 1 taken 67246 times.
2267437 if (LLIc == ' ')
648 {
649 2200191 LLIc = '0';
650 }
651 2267437 LLI = static_cast<uint8_t>(LLIc - '0');
652 }
653 2314987 curExtractLoc++; // Go over Loss of lock indicator (LLI)
654
655 // Signal Strength Indicator (SSI)
656 //
657 // Carrier to Noise ratio(RINEX) | Carrier to Noise ratio(dbHz)
658 // 1 (minimum possible signal strength) | < 12
659 // 2 | 12-17
660 // 3 | 18-23
661 // 4 | 24-29
662 // 5 (average/good S/N ratio) | 30-35
663 // 6 | 36-41
664 // 7 | 42-47
665 // 8 | 48-53
666 // 9 (maximum possible signal strength) | ≥ 54
667 // 0 or blank: not known, don't care | -
668 2314987 uint8_t SSI = 0;
669
2/2
✓ Branch 1 taken 2267395 times.
✓ Branch 2 taken 47565 times.
2314987 if (line.size() > curExtractLoc)
670 {
671
1/2
✓ Branch 1 taken 2267394 times.
✗ Branch 2 not taken.
2267395 char SSIc = line.at(curExtractLoc);
672
2/2
✓ Branch 0 taken 885715 times.
✓ Branch 1 taken 1381679 times.
2267394 if (SSIc == ' ')
673 {
674 885715 SSIc = '0';
675 }
676 2267394 SSI = static_cast<uint8_t>(SSIc - '0');
677 }
678 2314959 curExtractLoc++; // Go over Signal Strength Indicator (SSI)
679
680
4/6
✓ Branch 0 taken 610396 times.
✓ Branch 1 taken 591286 times.
✓ Branch 2 taken 503369 times.
✓ Branch 3 taken 610398 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2314959 switch (obsDesc.type)
681 {
682 610396 case NAV::vendor::RINEX::ObsType::C: // Code / Pseudorange
683
1/2
✓ Branch 2 taken 610403 times.
✗ Branch 3 not taken.
610382 (*gnssObs)({ obsDesc.code, satNum }).pseudorange = { .value = observation,
684 1220799 .SSI = SSI };
685 610391 break;
686 591286 case NAV::vendor::RINEX::ObsType::L: // Phase
687
1/2
✓ Branch 2 taken 591303 times.
✗ Branch 3 not taken.
591280 (*gnssObs)({ obsDesc.code, satNum }).carrierPhase = { .value = observation,
688 .SSI = SSI,
689 1182589 .LLI = LLI };
690 591289 break;
691 503369 case NAV::vendor::RINEX::ObsType::D: // Doppler
692
1/2
✓ Branch 4 taken 503373 times.
✗ Branch 5 not taken.
503369 (*gnssObs)({ obsDesc.code, satNum }).doppler = observation;
693 503373 break;
694 610398 case NAV::vendor::RINEX::ObsType::S: // Raw signal strength(carrier to noise ratio)
695
1/2
✓ Branch 4 taken 610423 times.
✗ Branch 5 not taken.
610398 (*gnssObs)({ obsDesc.code, satNum }).CN0 = observation;
696 610423 break;
697 case NAV::vendor::RINEX::ObsType::I:
698 case NAV::vendor::RINEX::ObsType::X:
699 case NAV::vendor::RINEX::ObsType::Error:
700 LOG_WARN("{}: ObsType {} not supported", nameId(), size_t(obsDesc.type));
701 break;
702 }
703
704
2/4
✓ Branch 1 taken 2315204 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 2315264 times.
✗ Branch 7 not taken.
2314986 gnssObs->satData(SatId{ satSys, satNum }).frequencies |= obsDesc.code.getFrequency();
705
706 LOG_DATA("{}: {}-{}-{}: {}, LLI {}, SSI {}", nameId(),
707 NAV::vendor::RINEX::obsTypeToChar(obsDesc.type), obsDesc.code, satNum,
708 observation, LLI, SSI);
709
710
4/6
✓ Branch 0 taken 2315223 times.
✓ Branch 1 taken 14 times.
✓ Branch 3 taken 2315201 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2315287 times.
✗ Branch 7 not taken.
2315237 if (_eraseLessPreciseCodes) { eraseLessPreciseCodes(gnssObs, obsDesc.code.getFrequency(), satNum); }
711 2683734 }
712
713
2/2
✓ Branch 3 taken 266806 times.
✓ Branch 4 taken 102 times.
266772 if (gnssObs->data.back().pseudorange)
714 {
715 266806 if (!gnssObs->data.back().carrierPhase)
716 {
717 LOG_DATA("{}: A data record at epoch {} (plus leap seconds) contains Pseudorange, but is missing carrier phase.", nameId(), epochTime.toYMDHMS());
718 }
719 266799 if (!gnssObs->data.back().doppler)
720 {
721 LOG_DATA("{}: A data record at epoch {} (plus leap seconds) contains Pseudorange, but is missing doppler.", nameId(), epochTime.toYMDHMS());
722 }
723 266794 if (!gnssObs->data.back().CN0)
724 {
725 LOG_DATA("{}: A data record at epoch {} (plus leap seconds) contains Pseudorange, but is missing raw signal strength(carrier to noise ratio).", nameId(), epochTime.toYMDHMS());
726 }
727 }
728 266900 satCnt++;
729 }
730
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 8488 times.
8581 if (satCnt != nSatellites)
731 {
732
4/8
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 93 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 93 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 93 times.
✗ Branch 14 not taken.
93 LOG_WARN("{}: [{}] {} satellites read, but epoch header specified {} satellites", nameId(), gnssObs->insTime.toYMDHMS(GPST), satCnt, nSatellites);
733 }
734
735 8581 gnssObs->receiverInfo = _receiverInfo;
736
737
1/2
✓ Branch 2 taken 8584 times.
✗ Branch 3 not taken.
8584 invokeCallbacks(OUTPUT_PORT_INDEX_GNSS_OBS, gnssObs);
738 8584 return gnssObs;
739 8633 }
740
741 2315189 void RinexObsFile::eraseLessPreciseCodes(const std::shared_ptr<NAV::GnssObs>& gnssObs, const Frequency& freq, uint16_t satNum) // NOLINT(readability-convert-member-functions-to-static)
742 {
743 9748900 auto eraseLessPrecise = [&](const Code& third, const Code& second, const Code& prime) {
744 3551788 auto eraseSatDataWithCode = [&](const Code& code) {
745 LOG_DATA("{}: Searching for {}-{}", nameId(), code, satNum);
746
1/2
✓ Branch 2 taken 3551920 times.
✗ Branch 3 not taken.
3551788 auto iter = std::ranges::find_if(gnssObs->data, [code, satNum](const GnssObs::ObservationData& idData) {
747
1/2
✓ Branch 2 taken 185300334 times.
✗ Branch 3 not taken.
185035719 return idData.satSigId == SatSigId{ code, satNum };
748 });
749
2/2
✓ Branch 3 taken 47004 times.
✓ Branch 4 taken 3504954 times.
3551920 if (iter != gnssObs->data.end())
750 {
751 LOG_DATA("{}: Erasing {}-{}", nameId(), code, satNum);
752
1/2
✓ Branch 3 taken 47002 times.
✗ Branch 4 not taken.
47004 gnssObs->data.erase(iter);
753 }
754 3551956 };
755
756
3/4
✓ Branch 3 taken 9751964 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1344361 times.
✓ Branch 6 taken 8407603 times.
9748900 if (gnssObs->contains({ prime, satNum }))
757 {
758
1/2
✓ Branch 1 taken 1344347 times.
✗ Branch 2 not taken.
1344361 eraseSatDataWithCode(second);
759
1/2
✓ Branch 1 taken 1344353 times.
✗ Branch 2 not taken.
1344347 eraseSatDataWithCode(third);
760 }
761
3/4
✓ Branch 3 taken 8407391 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 863472 times.
✓ Branch 6 taken 7543919 times.
8407603 else if (gnssObs->contains({ second, satNum }))
762 {
763
1/2
✓ Branch 1 taken 863474 times.
✗ Branch 2 not taken.
863472 eraseSatDataWithCode(third);
764 }
765 9751746 };
766
767
8/11
✓ Branch 1 taken 2314954 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 761830 times.
✓ Branch 5 taken 815033 times.
✓ Branch 6 taken 376129 times.
✓ Branch 7 taken 286545 times.
✓ Branch 8 taken 45204 times.
✓ Branch 9 taken 4712 times.
✓ Branch 10 taken 25916 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
2315189 switch (SatelliteSystem_(freq.getSatSys()))
768 {
769 761830 case GPS:
770
4/8
✓ Branch 1 taken 761826 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 761849 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 761865 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 761880 times.
✗ Branch 11 not taken.
761830 eraseLessPrecise(Code::G1S, Code::G1L, Code::G1X); ///< L1C (data, pilot, combined)
771
4/8
✓ Branch 1 taken 761897 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 761891 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 761907 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 761886 times.
✗ Branch 11 not taken.
761880 eraseLessPrecise(Code::G2S, Code::G2L, Code::G2X); ///< L2C-code (medium, long, combined)
772
4/8
✓ Branch 1 taken 761877 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 761889 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 761898 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 761890 times.
✗ Branch 11 not taken.
761886 eraseLessPrecise(Code::G5I, Code::G5Q, Code::G5X); ///< L5 (data, pilot, combined)
773 761890 break;
774 815033 case GAL:
775
4/8
✓ Branch 1 taken 815003 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815030 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 815029 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 815067 times.
✗ Branch 11 not taken.
815033 eraseLessPrecise(Code::E1B, Code::E1C, Code::E1X); ///< OS (data, pilot, combined)
776
4/8
✓ Branch 1 taken 815065 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815073 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 815083 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 815062 times.
✗ Branch 11 not taken.
815067 eraseLessPrecise(Code::E5I, Code::E5Q, Code::E5X); ///< E5a (data, pilot, combined)
777
4/8
✓ Branch 1 taken 815067 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815072 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 815081 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 815062 times.
✗ Branch 11 not taken.
815062 eraseLessPrecise(Code::E6B, Code::E6C, Code::E6X); ///< E6 (data, pilot, combined)
778
4/8
✓ Branch 1 taken 815068 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815079 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 815083 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 815063 times.
✗ Branch 11 not taken.
815062 eraseLessPrecise(Code::E7I, Code::E7Q, Code::E7X); ///< E5b (data, pilot, combined)
779
4/8
✓ Branch 1 taken 815056 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815065 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 815068 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 815066 times.
✗ Branch 11 not taken.
815063 eraseLessPrecise(Code::E8I, Code::E8Q, Code::E8X); ///< E5 AltBOC (data, pilot, combined)
780 815066 break;
781 376129 case GLO:
782
4/8
✓ Branch 1 taken 376130 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 376117 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 376148 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 376174 times.
✗ Branch 11 not taken.
376129 eraseLessPrecise(Code::R3I, Code::R3Q, Code::R3X); ///< L3 (data, pilot, combined)
783
4/8
✓ Branch 1 taken 376178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 376177 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 376181 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 376172 times.
✗ Branch 11 not taken.
376174 eraseLessPrecise(Code::R4A, Code::R4B, Code::R4X); ///< G1a (data, pilot, combined)
784
4/8
✓ Branch 1 taken 376170 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 376179 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 376183 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 376177 times.
✗ Branch 11 not taken.
376172 eraseLessPrecise(Code::R6A, Code::R6B, Code::R6X); ///< G2a (data, pilot, combined)
785 376177 break;
786 286545 case BDS:
787
4/8
✓ Branch 1 taken 286542 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 286545 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 286542 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 286549 times.
✗ Branch 11 not taken.
286545 eraseLessPrecise(Code::B1D, Code::B1P, Code::B1X); ///< B1 (data, pilot, combined)
788
4/8
✓ Branch 1 taken 286549 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 286551 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 286549 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 286545 times.
✗ Branch 11 not taken.
286549 eraseLessPrecise(Code::B2I, Code::B2Q, Code::B2X); ///< B1I(OS), B1Q, combined
789
4/8
✓ Branch 1 taken 286545 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 286542 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 286546 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 286549 times.
✗ Branch 11 not taken.
286545 eraseLessPrecise(Code::B5D, Code::B5P, Code::B5X); ///< B2a (data, pilot, combined)
790
4/8
✓ Branch 1 taken 286546 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 286547 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 286545 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 286548 times.
✗ Branch 11 not taken.
286549 eraseLessPrecise(Code::B6I, Code::B6Q, Code::B6X); ///< B3I, B3Q, combined
791
4/8
✓ Branch 1 taken 286550 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 286551 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 286550 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 286547 times.
✗ Branch 11 not taken.
286548 eraseLessPrecise(Code::B7I, Code::B7Q, Code::B7X); ///< B2I(OS), B2Q, combined
792
4/8
✓ Branch 1 taken 286548 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 286549 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 286551 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 286543 times.
✗ Branch 11 not taken.
286547 eraseLessPrecise(Code::B7D, Code::B7P, Code::B7Z); ///< B2b (data, pilot, combined)
793
4/8
✓ Branch 1 taken 286546 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 286549 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 286547 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 286545 times.
✗ Branch 11 not taken.
286543 eraseLessPrecise(Code::B8D, Code::B8P, Code::B8X); ///< B2 (B2a+B2b) (data, pilot, combined)
794 286545 break;
795 45204 case QZSS:
796
4/8
✓ Branch 1 taken 45204 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 45204 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 45204 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 45204 times.
✗ Branch 11 not taken.
45204 eraseLessPrecise(Code::J1S, Code::J1L, Code::J1X); ///< L1C (data, pilot, combined)
797
4/8
✓ Branch 1 taken 45204 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 45204 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 45204 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 45203 times.
✗ Branch 11 not taken.
45204 eraseLessPrecise(Code::J2S, Code::J2L, Code::J2X); ///< L2C-code (medium, long, combined)
798
4/8
✓ Branch 1 taken 45204 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 45204 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 45204 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 45204 times.
✗ Branch 11 not taken.
45203 eraseLessPrecise(Code::J5I, Code::J5Q, Code::J5X); ///< L5 (data, pilot, combined)
799
4/8
✓ Branch 1 taken 45204 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 45204 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 45204 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 45204 times.
✗ Branch 11 not taken.
45204 eraseLessPrecise(Code::J5D, Code::J5P, Code::J5Z); ///< L5 (data, pilot, combined)
800
4/8
✓ Branch 1 taken 45203 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 45204 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 45204 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 45204 times.
✗ Branch 11 not taken.
45204 eraseLessPrecise(Code::J6S, Code::J6L, Code::J6X); ///< LEX signal (short, long, combined)
801 45204 break;
802 4712 case IRNSS:
803
4/8
✓ Branch 1 taken 4712 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4712 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4712 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4712 times.
✗ Branch 11 not taken.
4712 eraseLessPrecise(Code::I5B, Code::I5C, Code::I5X); ///< RS (data, pilot, combined)
804
4/8
✓ Branch 1 taken 4712 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4712 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4712 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4712 times.
✗ Branch 11 not taken.
4712 eraseLessPrecise(Code::I9B, Code::I9C, Code::I9X); ///< RS (data, pilot, combined)
805 4712 break;
806 25916 case SBAS:
807
4/8
✓ Branch 1 taken 25916 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25916 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25916 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 25916 times.
✗ Branch 11 not taken.
25916 eraseLessPrecise(Code::S5I, Code::S5Q, Code::S5X); ///< L5 (data, pilot, combined)
808 25916 break;
809 case SatSys_None:
810 break;
811 }
812 2315096 }
813
814 } // namespace NAV
815